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 = {
|
||||
/** Stable identifier for this user. Joins events across sessions + devices. */
|
||||
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 {
|
||||
/** Emit a named event. The conformance gate cross-checks `event` against the manifest's analyticsEvents. */
|
||||
track(
|
||||
event: string,
|
||||
properties?: Record<string, AnalyticsAttributeValue>,
|
||||
user?: AnalyticsUser,
|
||||
attributes?: Record<string, AnalyticsAttributeValue>,
|
||||
): 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. */
|
||||
pageView(
|
||||
path: string,
|
||||
properties?: Record<string, AnalyticsAttributeValue>,
|
||||
attributes?: Record<string, AnalyticsAttributeValue>,
|
||||
): void;
|
||||
|
||||
/** 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 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)
|
||||
- Retention policy in the analytics backend
|
||||
- Trait allowlist enforcement in their application code (if they want stricter than "permissive")
|
||||
- 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).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user