docs(compliance): add DSR guide, consent guide, subject-linkage example, glossary terms
- docs/guides/dsr.md: GDPR Art. 15/16/17/18/20 interface mapping, tRPC router wiring, multi-subject handling, soft vs cascade-hard semantics, DeletionCertificate format and storage requirements - docs/guides/consent.md: requiresConsent manifest field, withConsent DI wiring, runtime isGranted pattern, IConsent audit trail, anonymous→ authenticated migration, cookie _v versioning, SSR-safe banner loading, CNIL/EDPB equal-prominence requirement - docs/compliance/subject-linkage.example.md: SubjectLink kind discriminator with worked support-ticket example (owner submitter + reference assignee) - docs/glossary.md: SubjectLink, DeletionCertificate, UserConsentState, ConsentChecked entries; Manifest definition updated with requiresConsent - CLAUDE.md: lint comment 8→12 conformance rules; conformance section notes requiresConsent; brand composition order updated to full 5-wrapper chain - docs/guides/conformance-quickref.md: requiresConsent field added to manifest table; component-must-have-story, component-must-have-test, atomic-tier-import-direction added to ESLint rules table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -78,7 +78,7 @@ A port for collection-style entity access. Lives in `infrastructure/<x>.reposito
|
||||
A port for non-collection capabilities (auth, mailer, etc.). Same `.ts` / `.mock.ts` / `.interface.ts` triad as repositories.
|
||||
|
||||
**Manifest** (`feature.manifest.ts`):
|
||||
The per-feature contract declaring `useCases`, `audits`, `publishes`, `consumes`, and `requiredCores`. Source of truth for the conformance system. Always edited **before** code (manifest-first ordering).
|
||||
The per-feature contract declaring `useCases`, `audits`, `publishes`, `consumes`, `requiredCores`, and (when applicable) `requiresConsent: ConsentCategory[]`. Source of truth for the conformance system. Always edited **before** code (manifest-first ordering).
|
||||
|
||||
**Symbol**:
|
||||
A DI token in `packages/<feature>/src/di/symbols.ts`. Used by Inversify to identify a binding.
|
||||
@@ -258,6 +258,14 @@ _Avoid:_ confusing analytics events with cross-feature events (bus) or audit eve
|
||||
**`withSpan`** / **`withCapture`** / **`withAudit`** / **`withAnalytics`** / **`withConsent`** / **`withRateLimit`**:
|
||||
The six wrapper composers applied at DI bind time. Composition is nested with `withSpan` outermost; each layer attaches a brand: `withSpan` → `Instrumented`, `withCapture` → `Captured`, `withAudit` → `Audited` (when manifest declares `audits`), `withAnalytics` → `Analyzed` (when manifest declares `analyticsEvents`), `withConsent` → `ConsentChecked` (when manifest declares `requiresConsent`), `withRateLimit` → `RateLimited` (when manifest declares `rateLimit`). See ADR-025 for the compliance-baseline channels.
|
||||
|
||||
**SubjectLink**:
|
||||
A field-level declaration in a Payload collection's `custom.subject` array that maps a relationship field to a data subject. Carries `field` (Payload field name), `kind` (`"self"` | `"owner"` | `"reference"`), optional `target` (auth-collection slug), and optional `role` (semantic label). The DSR cascade walks these at runtime to determine which rows to include in Art. 15 exports and which rows to erase or redact on Art. 17 requests. See `docs/compliance/subject-linkage.example.md`.
|
||||
_Avoid:_ confusing `kind: "self"` (the subject's own account row) with `kind: "owner"` (rows the subject authored/owns) — both are treated as owned data for DSR purposes, but `"self"` targets the auth-collection record itself.
|
||||
|
||||
**DeletionCertificate**:
|
||||
The immutable proof-of-erasure record returned by `IDataDelete.deleteSubjectData(subjectId, mode)`. Carries `subjectId` (or `"erased-{hash}"` after the ID itself is purged), `mode` (`"soft"` | `"cascade-hard"`), `timestamp` (ISO 8601), `reason`, `affected` (per-collection summary of `collection`, `rowsAffected`, `action`, and `fields` NULLed), and `auditEntryId` linking to the immutable audit log entry. GDPR Art. 17 compliance artifact — store permanently, never mutate. See `docs/guides/dsr.md`.
|
||||
_Avoid:_ generating a `DeletionCertificate` manually — it is emitted only by the production `IDataDelete` implementation to ensure correctness.
|
||||
|
||||
**DSR** (`@repo/core-dsr`):
|
||||
The Data Subject Rights handler — optional core package providing four interfaces that operate on user data in response to GDPR Arts. 15–18 requests. `IDataExport` (Art. 15 access + Art. 20 portability), `IDataDelete` (Art. 17 erasure cascade), `IDataRectify` (Art. 16), `IProcessingRestriction` (Art. 18). Sibling to `core-audit` — audit _records_ access, DSR _acts_ on the underlying data. Walks Payload collections at runtime using field-level `custom.pii` tags. See ADR-025.
|
||||
_Avoid:_ confusing DSR with audit; audit is the immutable journal, DSR is the mutator.
|
||||
@@ -265,6 +273,13 @@ _Avoid:_ confusing DSR with audit; audit is the immutable journal, DSR is the mu
|
||||
**Consent** (`@repo/core-consent`):
|
||||
The runtime gate for granular consent categories (essential/functional/analytics/marketing). Optional core package providing `IConsent` interface (`isGranted`/`grant`/`withdraw`/`getCategories`) + `ConsentChecked` brand + `requiresConsent: [...]` manifest field + `no-undeclared-consent-check` ESLint rule. Cookie banner UI ships as atomic component in `core-ui`. See ADR-025.
|
||||
|
||||
**UserConsentState**:
|
||||
A per-category consent record returned by `IConsent.getCategories()`. Carries `category` (`ConsentCategory`), `state` (`"granted"` | `"denied"` | `"pending"`), optional `grantedAt` and `withdrawnAt` ISO 8601 timestamps, `bannerVersion`, `policyVersion`, and `method` (how the category was consented — e.g., `"banner-accept"`, `"signup-migration"`). The element type of the `getCategories()` return value; used for compliance audits and cookie-versioning migration-on-read logic.
|
||||
|
||||
**ConsentChecked**:
|
||||
The phantom-type brand `ConsentChecked<F>` attached by `withConsent(consent, factory(deps))` at DI bind time. Signals that the wrapped use case's `requiresConsent` categories will be checked before execution. TypeScript rejects binding an unwrapped factory to a `ConsentChecked`-typed DI symbol when the manifest declares `requiresConsent`. The boot assertion (`assertFeatureConformance`) verifies the brand is present at runtime. See `withConsent` in `@repo/core-consent`.
|
||||
_Avoid:_ applying `withConsent` in test files — tests inject mocks directly into the factory and do not go through the DI wrappers.
|
||||
|
||||
**Rate-limit** (`core-shared/rate-limit`):
|
||||
Fourth conformance channel after audit/analytics/consent. `IRateLimit` interface (`consume`/`reset`) + `withRateLimit` wrapper + `RateLimited` brand + `rateLimit: { window, budget }` manifest field. Default budgets in manifest, runtime overrides via `ctx.rateLimit` config. Consumer wires Redis/Upstash impl. See ADR-025.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user