docs(tooling): add sub-processor discriminated union to ADR-022 and traces
Amends ADR-022 §9 with the `is-sub-processor` / `processes-pii` discriminated union spec, including the five conditional fields required when a library is a true GDPR sub-processor. Updates the evaluate-library skill to prompt for these fields during every trace authoring pass and adds the updated frontmatter template. Backfills all nine existing library-decision traces with the new fields; payload gets `processes-pii: true` (self-hosted CMS that stores user data); all pure in-process libraries get `false / false`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -178,13 +178,69 @@ Name at least two alternatives evaluated before choosing this library. For `core
|
||||
|
||||
---
|
||||
|
||||
## Sub-processor classification
|
||||
|
||||
Answer these two questions before writing the trace. The answers become
|
||||
top-level frontmatter fields required by ADR-022 §9.
|
||||
|
||||
### Question: is-sub-processor
|
||||
|
||||
**Does the vendor receive personal data on the operator's behalf?**
|
||||
|
||||
A library is a sub-processor when it transmits personal data (user identifiers,
|
||||
email addresses, behavioural events, request bodies, etc.) to a vendor-controlled
|
||||
endpoint — analytics SDKs, error-tracking clients, AI APIs, log aggregation
|
||||
services. Network calls alone do not make a library a sub-processor; only calls
|
||||
that carry personal data do.
|
||||
|
||||
Pure in-process libraries (no network calls), self-hostable software where the
|
||||
operator controls the endpoint, and build-time-only tools are **not** sub-processors.
|
||||
|
||||
Set `is-sub-processor: true | false`.
|
||||
|
||||
### Question: processes-pii
|
||||
|
||||
**Does the library process personal data in-process, even without transmitting
|
||||
it to a vendor?**
|
||||
|
||||
A library processes PII if it reads, validates, serialises, stores, or
|
||||
transforms data fields that may contain personal information (names, emails,
|
||||
IDs, content authored by users, authentication credentials). A self-hosted
|
||||
database or CMS is a prime example: no data leaves to a vendor, yet the
|
||||
library clearly handles PII.
|
||||
|
||||
Pure utility libraries (DI containers, type validators, serialisers operating
|
||||
on already-typed objects without inspecting field semantics, test runners)
|
||||
typically answer `false`.
|
||||
|
||||
Set `processes-pii: true | false`.
|
||||
|
||||
### Conditional block: when is-sub-processor is true
|
||||
|
||||
When `is-sub-processor: true`, five additional fields are **required** in the
|
||||
trace frontmatter. Gather them before writing the trace:
|
||||
|
||||
```
|
||||
data-sent: "<what personal data the library transmits to the vendor>"
|
||||
region: "<vendor data region, e.g. eu-west-1 or eu>"
|
||||
dpa-signed: true | false # has the operator signed a DPA with this vendor?
|
||||
sccs-required: true | false # does the vendor require SCCs (non-EEA transfer)?
|
||||
contact: "<vendor DPO or privacy contact email/URL>"
|
||||
```
|
||||
|
||||
If the vendor does not yet have a signed DPA or if you cannot determine the
|
||||
region, record `dpa-signed: false` / region as best-known and add a prose note
|
||||
under `## Sub-processor` in the trace body explaining the gap.
|
||||
|
||||
---
|
||||
|
||||
## Trace write step
|
||||
|
||||
Write the trace **unconditionally** at evaluation end — even for rejections, even for partial traces.
|
||||
|
||||
**Path:** `docs/library-decisions/<YYYY-MM-DD>-<package-name>.md`
|
||||
|
||||
Use today's date. Use the `TRACE-TEMPLATE.md` in this directory as the structural guide.
|
||||
Use today's date. Use `docs/library-decisions/_template.md` as the structural guide.
|
||||
|
||||
Frontmatter rules:
|
||||
|
||||
@@ -192,8 +248,46 @@ Frontmatter rules:
|
||||
- `adr: null` for feature-tier. For core-tier approvals, coordinate the ADR slug before writing (`adr: adr-NNN`).
|
||||
- `verification-commands` — include the literal commands run for each filter, one per line.
|
||||
- `accepted-cves: []` (empty unless you accepted a specific advisory).
|
||||
- `is-sub-processor` and `processes-pii` are **always required** (see Sub-processor classification above).
|
||||
- When `is-sub-processor: true`, include `data-sent`, `region`, `dpa-signed`, `sccs-required`, and `contact`.
|
||||
- For skipped expensive filters, write `skip` for the frontmatter value and omit the prose section body or note "Not evaluated — skipped due to earlier rejection."
|
||||
|
||||
Frontmatter template:
|
||||
|
||||
```yaml
|
||||
---
|
||||
package: <name>
|
||||
version: "<semver range>"
|
||||
tier: app | feature | core
|
||||
decision: approved | rejected
|
||||
date: <YYYY-MM-DD>
|
||||
deciders: [<author>, ...]
|
||||
adr: adr-NNN | null
|
||||
lastRevalidated: null
|
||||
is-sub-processor: false
|
||||
processes-pii: false
|
||||
# include the block below only when is-sub-processor: true
|
||||
# data-sent: "<description>"
|
||||
# region: "<eu | eu-west-1 | ...>"
|
||||
# dpa-signed: false
|
||||
# sccs-required: false
|
||||
# contact: "<url or email>"
|
||||
filter-results:
|
||||
license: <SPDX id>
|
||||
types: native | "@types/<x>" | none
|
||||
maintenance: active | dormant | abandoned
|
||||
boundary-fit: pass | fail
|
||||
shadow-check: pass | fail | "shadows <x>"
|
||||
eu-residency: ok | n/a | self-hostable | fail
|
||||
cve-scan: clean | "<advisory-id>" | fail
|
||||
named-consumer: pass | fail
|
||||
socketRisk: clean | flagged | <arbitrary-string>
|
||||
verification-commands:
|
||||
- <literal command that produced each filter result>
|
||||
accepted-cves: []
|
||||
---
|
||||
```
|
||||
|
||||
After writing the trace:
|
||||
|
||||
- For approved traces: confirm the trace is staged in the same commit as the `package.json` change. The pre-commit hook validates this.
|
||||
|
||||
Reference in New Issue
Block a user