ADR-025 plans 4 epics that raise the template's DPA/GDPR coverage from ~50% (ADR-017/018/022/023/024) to ~80%. Adds two optional cores (core-dsr, core-consent), a fourth conformance channel (rate-limit), three new manifest fields, three new generators with CI drift gates, and a docs/compliance/ + compliance/ split for templates vs evidence. Documents the audit ↔ DSR sibling-not-overlap distinction (audit records access; DSR acts on the underlying data). Four explicit deferrals with revisit triggers (RBAC, MFA, breach detection, Art. 22). Glossary updated with DSR, Consent, Rate-limit, PII inventory, Retention policy, Sub-processor, docs/compliance/ vs compliance/ entries.
346 lines
24 KiB
Markdown
346 lines
24 KiB
Markdown
## ADR-025 — EU compliance baseline (DPA/GDPR template scope)
|
|
|
|
**Status:** Accepted
|
|
**Date:** 2026-05-18
|
|
**Related:** ADR-006 (vertical feature packages), ADR-017 (OTel + observability PII boundary), ADR-018 (audit + compliance), ADR-022 (library evaluation policy — extended here for sub-processors), ADR-023 (CI security + supply chain — SBOM amended here), ADR-024 (product analytics channel)
|
|
**Companion PRDs** (one per epic, sequenced):
|
|
|
|
- `docs/work/prds/compliance-manifests-pii-retention-subprocessors.prd.md` (Epic A)
|
|
- `docs/work/prds/dsr-consent-and-cookie-banner.prd.md` (Epic B)
|
|
- `docs/work/prds/security-headers-rate-limit-sbom.prd.md` (Epic C)
|
|
- `docs/work/prds/compliance-docs-scaffolds.prd.md` (Epic D)
|
|
|
|
## Context
|
|
|
|
A DPA/GDPR compliance playbook (22 sections, stack-agnostic) was reviewed against the template's current state on **2026-05-18**. The audit produced a clean three-way split:
|
|
|
|
1. **Already covered** by prior ADRs:
|
|
- PII boundary on observability (ADR-017 §7 — `sendDefaultPii: false` CI gate, server-side scrubbing, replay masking)
|
|
- Audit logging baseline (ADR-018 — `@repo/core-audit`, DPA-aligned schema, `eraseSubject` pseudonymization)
|
|
- EU library residency (ADR-022 — hard filter in `/evaluate-library`)
|
|
- Supply-chain + CI security (ADR-023 — Renovate SHA pinning, Socket.dev, audit signatures, CodeQL, gitleaks, trace revalidation)
|
|
- Analytics PII deferral (ADR-024 — explicit consumer-policy boundary)
|
|
|
|
2. **Template-shaped gap** — 10 items the playbook flagged that the template can codify _before_ any consumer adopts it. These are conformance-pattern shaped (manifest fields, brands, ESLint rules, generators) or scaffolding (interfaces, default middleware, fill-in docs).
|
|
|
|
3. **Product-shaped (deferred)** — 3 items that need product shape before being meaningful. Each has a documented trigger condition for revisit.
|
|
|
|
The motivating pressure: a consumer adopting this template today gets ~50% of the playbook's surface for free. The 10-item template-shaped gap is what this ADR plans (raising coverage to ~80%); the remaining ~20% product/process/legal scope stays consumer-owned and is documented as such.
|
|
|
|
Audit + DSR is the canonical confusion to flag upfront: **audit _records_ personal-data access (immutable journal); DSR _acts_ on the underlying data in response to user requests (mutator/exporter).** They are sibling concerns, not duplicates.
|
|
|
|
## Decision
|
|
|
|
Ship the 10 template-shaped items across **four epics**, sequenced A → B → D with C interleaved opportunistically. Defer 3 items explicitly. Add 3 new manifest fields, 2 new optional cores, 3 new ESLint rules.
|
|
|
|
### The 4 epics
|
|
|
|
#### Epic A — Declarative compliance manifests
|
|
|
|
**Items:** PII inventory + data retention + sub-processor inventory.
|
|
|
|
Three declarative artifacts, each driven by a different surface and a generator that emits the audit-evidence YAML:
|
|
|
|
| Artifact | Declaration site | Generator output |
|
|
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
|
| PII inventory | `custom.pii: { category, purpose, retention, exportable, restrictable }` per Payload **field** | `compliance/data-map.yml` |
|
|
| Retention policy | `custom.retention: { activeRetention, postDeletion, purgeSchedule, hardDeleteAfter }` per Payload **collection** | `compliance/retention-policy.yml` |
|
|
| Sub-processor inventory | Extended ADR-022 library traces — frontmatter fields `is-sub-processor`, `processes-pii`, `data-sent`, `region`, `dpa-signed`, `sccs-required`, `contact` | `compliance/sub-processors.yml` |
|
|
|
|
**Key design decisions:**
|
|
|
|
- **PII at the field level, not the manifest level.** PII is a storage question ("what personal data does this system hold?"), not an action question. Existing manifest fields (`audits`, `publishes`, `analyticsEvents`) are all event-shaped — emissions from use cases. PII fields don't fit that shape. Tagging at the field level puts the metadata where DSR consumes it at runtime.
|
|
|
|
- **Retention at the collection level, not per use case.** Same rationale — retention is a storage property. Per-field PII retention overrides apply where the PII tag specifies a stricter retention than the collection default (more-specific wins). Background purge job in `core-shared/jobs` reads collection config at boot.
|
|
|
|
- **Sub-processors via ADR-022 traces, not standalone file.** Every direct-dep library trace already records EU residency, license, CVE acceptance. Extending it with the sub-processor fields (DPA signed date, SCCs, contact, PII processed, data sent, region) unifies two related obligations in one record. Pure-HTTP sub-processors (REST calls without an SDK) get hand-authored entries with no backing trace as an exception, CI-flagged.
|
|
|
|
The three generators run via `pnpm compliance:emit-all`. CI gate verifies generator output matches the source declarations (drift detection).
|
|
|
|
**Background purge job:** `core-shared/jobs/retention-purge.job.ts`. Reads `custom.retention` from each collection at boot; schedules per-collection purge cadence; emits an `IAuditLog.record({ action: "DELETE", reason: "retention-policy" })` audit entry per row purged.
|
|
|
|
#### Epic B — DSR + consent + cookie banner
|
|
|
|
**Items:** DSR scaffold + consent abstraction + cookie consent UI.
|
|
|
|
Builds the user-rights surface end-to-end:
|
|
|
|
**`@repo/core-dsr`** — new optional core. Four interfaces:
|
|
|
|
```ts
|
|
interface IDataExport {
|
|
exportSubjectData(
|
|
subjectId: string,
|
|
format: "json" | "json-ld",
|
|
): Promise<UserDataBundle>;
|
|
}
|
|
interface IDataDelete {
|
|
deleteSubjectData(
|
|
subjectId: string,
|
|
mode: "soft" | "cascade-hard",
|
|
): Promise<DeletionCertificate>;
|
|
}
|
|
interface IDataRectify {
|
|
updateSubjectField(
|
|
subjectId: string,
|
|
collection: string,
|
|
field: string,
|
|
value: unknown,
|
|
): Promise<void>;
|
|
}
|
|
interface IProcessingRestriction {
|
|
setRestriction(subjectId: string, granted: boolean): Promise<void>;
|
|
isRestricted(subjectId: string): Promise<boolean>;
|
|
}
|
|
```
|
|
|
|
DSR ops walk Payload collections at runtime, using the field-level `custom.pii` tags from Epic A. `IDataExport` walks fields tagged `exportable: true`; `IDataDelete` walks all PII fields and cascades; `IProcessingRestriction` writes a flag on the user record that every read path checks.
|
|
|
|
**Scope cuts on DSR:**
|
|
|
|
- Art. 20 (portability) folded into Art. 15 (access) — same `IDataExport` with format option
|
|
- Art. 21 (objection) → consent epic via `IConsent.withdraw`
|
|
- Art. 22 (automated decision-making) → deferred (no ML in template; future ADR when a consumer adds automated decisions)
|
|
|
|
DSR ops are themselves PII access events — every `IDataExport`/`IDataDelete`/`IDataRectify` call writes an audit entry. After `IDataDelete`, `core-audit.IAuditLog.eraseSubject(actorId, "pseudonymize")` scrubs the audit trail (preserves the events, removes the identifier).
|
|
|
|
**`@repo/core-consent`** — new optional core. Sibling channel parallel to audit/analytics:
|
|
|
|
```ts
|
|
interface IConsent {
|
|
isGranted(subjectId: string, category: ConsentCategory): Promise<boolean>;
|
|
grant(
|
|
subjectId: string,
|
|
categories: ConsentCategory[],
|
|
record: ConsentRecord,
|
|
): Promise<void>;
|
|
withdraw(subjectId: string, categories: ConsentCategory[]): Promise<void>;
|
|
getCategories(subjectId: string): Promise<ConsentCategory[]>;
|
|
}
|
|
```
|
|
|
|
`ConsentCategory` is a consumer-typed string-literal-union (default: `"essential" | "functional" | "analytics" | "marketing"`, extensible). Conformance treatment:
|
|
|
|
- Brand: `ConsentChecked` attached by `withConsent` wrapper at bind time
|
|
- Manifest field: `requiresConsent: ["analytics"]` per use case
|
|
- ESLint rule: `no-undeclared-consent-check` cross-checks `consent.requires("X")` literal calls
|
|
- Boot assertion: `assertFeatureConformance` requires `ConsentChecked` brand when `requiresConsent.length > 0`
|
|
|
|
Consent grant/withdraw events are themselves audited (`auditLog.record({ action: "CONSENT_GRANT", category: "marketing" })`).
|
|
|
|
**`<CookieConsentBanner>` in `@repo/core-ui`** — atomic component. Default visual treatment baked for EU prominence requirements (Reject + Accept side-by-side, same size, equal prominence per EU regulator guidance). Granular (essential/functional/analytics/marketing). Consumer wires `IConsent` via React context. Storybook story doubles as human reading-room for compliant UX.
|
|
|
|
**Endpoint scaffolds:** `/api/gdpr/{export,delete,rectify,restrict}` — consumer-wireable routes. Live in `apps/web-next/app/api/gdpr/` and `apps/web-tanstack/src/routes/api/gdpr/`.
|
|
|
|
#### Epic C — Security hardening
|
|
|
|
**Items:** Security headers middleware + rate-limit primitive + SBOM in CI.
|
|
|
|
Small individual scope; grouped for dispatch efficiency. Each item is independent.
|
|
|
|
- **Security headers middleware** — default Next.js + TanStack middleware shipping HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy. CSP customizable per consumer (separate config; default is restrictive). Lives in `core-shared/security` plus per-framework re-export.
|
|
|
|
- **Rate-limit primitive** — fourth conformance channel after audit/analytics/consent. `IRateLimit` interface in `core-shared/rate-limit`:
|
|
|
|
```ts
|
|
interface IRateLimit {
|
|
consume(
|
|
key: string,
|
|
weight?: number,
|
|
): Promise<{ allowed: boolean; remaining: number; resetAt: Date }>;
|
|
reset(key: string): Promise<void>;
|
|
}
|
|
```
|
|
|
|
- Brand: `RateLimited` attached by `withRateLimit` wrapper
|
|
- Manifest field: `rateLimit: { window: "1m", budget: 60 }` per use case (defaults; runtime overrides via `ctx.rateLimit` config)
|
|
- ESLint rule: `no-undeclared-rate-limit` warns when a use case in an auth/write/export category lacks a `rateLimit` declaration
|
|
- Boot assertion: `assertFeatureConformance` requires `RateLimited` brand when `rateLimit` is set
|
|
- Consumer wires Redis/Upstash impl; `NoopRateLimit` always-allows for tests + dev
|
|
|
|
Rate-limit budgets at the manifest level are _defaults_ — overridable at runtime via `ctx.rateLimit` config (mirrors how analytics backend is consumer-wired). Manifest declaration is for the binding gate; runtime values are deployment-environment-specific.
|
|
|
|
- **SBOM in CI** — `cyclonedx-npm` step in `ci.yml`, artifact uploaded per release. Amendment to ADR-023.
|
|
|
|
#### Epic D — Compliance docs scaffolds
|
|
|
|
**Items:** Fill-in templates for runbooks, policies, and the pre-launch checklist.
|
|
|
|
Pure docs work. Lands last so it references the manifest fields, interfaces, and middleware shipped by A/B/C.
|
|
|
|
**Template-shipped (under `docs/compliance/`):**
|
|
|
|
- `data-map.example.yml` — generator-output reference
|
|
- `retention-policy.example.yml` — generator-output reference
|
|
- `sub-processors.example.yml` — generator-output reference
|
|
- `incident-runbook.template.md` (fill-in)
|
|
- `dsr-procedure.template.md`
|
|
- `backup-policy.template.md`
|
|
- `password-policy.template.md`
|
|
- `device-policy.template.md`
|
|
- `onboarding.template.md`
|
|
- `offboarding.template.md`
|
|
- `README.md` (explains the `docs/compliance/` vs `compliance/` split)
|
|
|
|
**Consumer-created (under `compliance/` at repo root):**
|
|
|
|
- `data-map.yml` (generator output)
|
|
- `retention-policy.yml` (generator output)
|
|
- `sub-processors.yml` (generator output from extended ADR-022 traces)
|
|
- `*.md` (filled-in copies of templates)
|
|
|
|
**Plus:** `docs/guides/pre-launch-compliance-checklist.md` — playbook §19 verbatim with template-specific wiring noted.
|
|
|
|
### Sequencing
|
|
|
|
**Order: A → B → D, with C interleaved opportunistically.**
|
|
|
|
Hard dependencies:
|
|
|
|
- B's `IDataExport`/`IDataDelete` walk Epic A's PII tags at runtime → A must finish before B story 1
|
|
- D's `data-map.example.yml` documents Epic A's PII schema → A's design must be settled before D
|
|
- D's `dsr-procedure.template.md` references Epic B's endpoints → B should be design-settled before D's PRD is decomposed
|
|
|
|
C is dependency-free; sandcastle picks C stories during gaps in A/B/D.
|
|
|
|
### Deferrals (explicit, with revisit triggers)
|
|
|
|
| Deferred | Why deferred | Trigger to revisit |
|
|
| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
| **RBAC primitive** (roles + permissions + tenant scoping) | Needs product-side decisions: which roles exist, multi-tenant or not, permission granularity | First downstream consumer ships with a stable role model |
|
|
| **MFA + password policy + lockout** (`auth` feature extension) | Needs identity-infrastructure choices (TOTP/WebAuthn), threat-model-specific policy values, OTP delivery vendor (ADR-022 territory) | First downstream consumer establishes auth threat model |
|
|
| **Breach detection patterns** (failed-login burst, bulk-access anomaly, off-hours admin) | Needs real auth flows, analytics backend, on-call infrastructure, product-specific anomaly definitions | First downstream consumer has live traffic + observability backend |
|
|
| **GDPR Art. 22** (automated decision-making) — sub-deferral within Epic B | Template has no ML/automated decisions | First downstream consumer adds automated decisions |
|
|
|
|
Each deferral has a documented trigger so the decision-when can be answered by the consumer, not the template authors.
|
|
|
|
### Consumer-scope items (explicitly out of template)
|
|
|
|
These appear in the playbook but are NOT template-shaped:
|
|
|
|
- **Infrastructure (§1, §12)** — EU region pinning of compute/storage/backups, TLS at deploy edge, encryption-at-rest config, VPN/bastion network boundaries, backup strategy + restore testing
|
|
- **Legal (§17)** — DPA, Privacy Policy, ToS, SCCs for non-EU sub-processors, DPIA artifacts, RoPA documents
|
|
- **Organizational (§14, §15)** — MDM enrollment, HR onboarding/offboarding scripts (the _script_ is template; the _policy_ is consumer), NDAs, security training, background checks, quarterly access reviews, pentest scheduling
|
|
|
|
Epic D ships fill-in templates for some documentation artifacts above; the values stay consumer-filled.
|
|
|
|
### Manifest schema impact
|
|
|
|
Per-use-case fields grow from 5 to 7:
|
|
|
|
- Existing: `mutates`, `audits`, `publishes`, `consumes`, `analyticsEvents`
|
|
- Added by ADR-025: `requiresConsent`, `rateLimit`
|
|
|
|
Per-Payload-collection `custom` config grows:
|
|
|
|
- Added by ADR-025: `pii` (per field), `retention` (per collection)
|
|
|
|
Per-library-trace frontmatter grows (extends ADR-022):
|
|
|
|
- Added by ADR-025: `is-sub-processor`, `processes-pii`, `data-sent`, `region`, `dpa-signed`, `sccs-required`, `contact`
|
|
|
|
### Conformance ESLint rule impact
|
|
|
|
Rule count: 7 → 10. New rules at warn severity (matching the audit/analytics-event convention):
|
|
|
|
- `no-undeclared-consent-check` — `consent.requires(...)` literal calls must match manifest's `requiresConsent`
|
|
- `no-undeclared-rate-limit` — auth/write/export categorized use cases without `rateLimit` field
|
|
- `pii-declaration-must-be-complete` — Payload `pii: true` fields missing required sub-keys (category, purpose, retention)
|
|
|
|
### Generator + CI gate inventory
|
|
|
|
| Generator | Source | Output | CI gate |
|
|
| ---------------------------------- | ----------------------------------------------------------- | --------------------------------- | -------------------------------------------- |
|
|
| `pnpm compliance:data-map` | Payload field `custom.pii` | `compliance/data-map.yml` | output matches collections (drift detection) |
|
|
| `pnpm compliance:retention-policy` | Payload collection `custom.retention` | `compliance/retention-policy.yml` | output matches collections |
|
|
| `pnpm compliance:sub-processors` | `docs/library-decisions/*.md` with `is-sub-processor: true` | `compliance/sub-processors.yml` | output matches traces |
|
|
| `pnpm compliance:emit-all` | runs all three | three files | runs all three CI gates |
|
|
|
|
## Alternatives considered
|
|
|
|
### A. One mega-epic covering all 10 items
|
|
|
|
Single PRD, single epic, ~30-40 stories. Pros: tight coupling across the manifest-schema changes; one review surface. Cons: enormous PR backlog with no natural checkpoint; reviewer fatigue; if half-shipped, the partial state leaves an ambiguous compliance surface.
|
|
|
|
**Rejected.** The four-epic split keeps each PRD focused enough for a useful single-document review.
|
|
|
|
### B. Per-item ADRs (10 ADRs)
|
|
|
|
One ADR per item — finer granularity. Pros: each architectural decision recorded in isolation. Cons: 10 ADRs to maintain, much repetition (each restates the playbook context), no unifying strategy doc, harder to answer cross-cutting questions like "why these 3 deferrals" without re-reading 4+ ADRs.
|
|
|
|
**Rejected.** ADR-025 is the unifying strategy; per-epic PRDs handle implementation specifics. Future deepening decisions on individual items can spawn their own ADRs as needed (e.g., when DSR cascade semantics need a specific architectural call, that becomes ADR-NNN).
|
|
|
|
### C. Wait until the first downstream consumer asks
|
|
|
|
Don't build any of this until a consumer arrives with a real compliance requirement.
|
|
|
|
**Rejected.** The whole value proposition of this template is that DPA/GDPR-shaped consumers don't have to invent these surfaces. The "named-consumer-now" rule from ADR-022 applies to _library adoption_, not to template surface — the consumer of this surface is "every downstream EU-bound product," which is real and immediate.
|
|
|
|
### D. PII declared at use-case manifest level instead of Payload field level
|
|
|
|
`pii: [{ category, purpose, retention }]` per use case, mirroring audit/publishes.
|
|
|
|
**Rejected.** Duplicates metadata across every use case touching the same field. Wrong semantic layer — PII is a storage question, not an action question. DSR (Epic B) needs runtime access to PII tags at the field level to walk Payload collections; manifest-level tags would require synthesis at runtime.
|
|
|
|
### E. DSR split across multiple cores (`@repo/core-data-export`, `@repo/core-data-delete`, etc.)
|
|
|
|
Maximum granularity. Pros: consumers adopt only what they need. Cons: 4+ packages to scaffold, tight coupling in practice (delete cascade needs to know export's PII tags), overkill — each "core" would have one interface.
|
|
|
|
**Rejected.** One `@repo/core-dsr` with 4 interfaces, mirroring `core-shared`'s tracer/logger/metrics packaging pattern (multiple interfaces in one package). Opt-in is at the package level, not the interface level.
|
|
|
|
### F. Consent folded into `core-audit` or `core-analytics`
|
|
|
|
`IConsent` added to an existing core.
|
|
|
|
**Rejected.** Audit _records_, consent _gates_ — different abstractions. Conflates package purpose. Analytics is only one of many consent-gated channels (marketing, profiling, cookies, third parties); putting consent inside analytics is too narrow.
|
|
|
|
### G. Rate-limit as interface-only (no brand)
|
|
|
|
Just an `IRateLimit` contract. Consumers call `rateLimit.consume(...)` where they need.
|
|
|
|
**Rejected.** Rate-limit drift would only surface when traffic hits — way too late. The five-latency drift detection is the template's signature pattern. Skipping it for rate-limit when it's universally applicable to auth/write/export endpoints (per playbook §5) leaves a real hole. Manifest declaration at the binding gate + runtime budget override gives both static enforcement and deployment flexibility.
|
|
|
|
### H. Compliance docs all under `docs/compliance/` (no root `compliance/` directory)
|
|
|
|
Templates and live artifacts co-located.
|
|
|
|
**Rejected.** The template ships only the _shape_; the consumer fills in the _evidence_. Auditors expect `compliance/` at the repo root (matches playbook §16 + standard SOC 2/ISO 27001 audit prep). Splitting locations matches the template-vs-consumer mental model used throughout this session.
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
- **Compliance surface ~80% template-shipped.** From ~50% pre-ADR (audit + PII boundary + library residency + supply chain) to ~80% post-epics. Remaining 20% is consumer-scope by design.
|
|
- **Conformance pattern extended consistently.** Two new manifest fields (`requiresConsent`, `rateLimit`) plus two new collection-level `custom.*` extensions all follow the established pattern. Three new ESLint rules at warn level. Three new manifest-driven generators. New consumers learn the pattern once.
|
|
- **DPA audit posture improves materially.** Sub-processor inventory, retention policy, data map, DSR endpoints, cookie consent are concrete artifacts a regulator or customer audit expects.
|
|
- **Explicit deferrals prevent premature design.** RBAC, MFA, breach detection, Art. 22 won't be re-suggested by future agents — ADR-025 records the trigger conditions.
|
|
- **Two new optional cores** (`core-dsr`, `core-consent`) match the established pattern. Template-tiers grows by two; scaffold path is `pnpm turbo gen core-package <name>`.
|
|
- **Rate-limit gets first-class treatment.** Fourth conformance channel; auth/write/export endpoints can't ship without a declared budget.
|
|
- **The audit ↔ DSR distinction is documented.** Future agents won't conflate the two — glossary entries plus this ADR's "Context" section make the split explicit.
|
|
|
|
### Negative
|
|
|
|
- **Manifest schema grows substantially.** Per-use-case fields go from 5 to 7. Per-collection `custom` config gains two extensions. Doc burden in glossary + `conformance-quickref.md` increases proportionally.
|
|
- **Conformance ESLint rule count: 7 → 10.** CLAUDE.md and quickref need rule-table updates each epic.
|
|
- **Two new optional cores to maintain.** Each needs versioning + CHANGELOG (per ADR-021).
|
|
- **`compliance/` directory becomes a new root location.** Adds a top-level directory alongside `docs/`, `packages/`, `apps/`. Consumers will see it; it's intentional but it's a new convention.
|
|
- **DSR cascade is non-trivial.** `IDataDelete` walking every Payload collection's PII fields requires Epic A's PII tags to be complete and correct. Epic B will surface gaps in Epic A's coverage during integration.
|
|
- **ADR-022 amendment.** Adding sub-processor fields to library traces is an extension to ADR-022's frontmatter spec. Existing traces need backfill (the weekly revalidation cron from ADR-023 will surface incomplete traces).
|
|
- **ADR-023 amendment.** SBOM generation step adds one workflow line; minor but counted.
|
|
|
|
### Neutral
|
|
|
|
- **No new CI gates beyond what generators introduce.** The three drift-detection gates (data-map, retention-policy, sub-processors) all reuse the existing CI workflow shape.
|
|
- **No vendor lock-in.** All interfaces (DSR cascade target, consent backend, rate-limit backend, security-header CSP values) remain consumer-decisions. Template ships interfaces + Noop/reference impls only.
|
|
- **Deferred items remain deferred.** ADR-025 doesn't preclude building RBAC/MFA/breach-detection/Art. 22 later — it just establishes that those decisions wait for product shape.
|
|
- **Cookie banner ships with opinionated EU-prominence defaults.** Consumers can override visual treatment but the default is the legally-defensible shape.
|
|
|
|
## Related
|
|
|
|
- ADR-006 — vertical feature packages (boundary tags new optional cores fit within)
|
|
- ADR-017 — OTel + observability PII boundary (the boundary `IConsent` does NOT cross — observability stays id-only)
|
|
- ADR-018 — audit + compliance (the sibling channel `core-dsr` complements without overlapping)
|
|
- ADR-022 — library evaluation policy (extended here with sub-processor frontmatter fields)
|
|
- ADR-023 — CI security + supply chain (SBOM amended here; rate-limit complements the supply-chain stack)
|
|
- ADR-024 — product analytics channel (sibling capture channel; `IConsent` from Epic B will gate `IAnalytics.track` calls in consumer products)
|