You keep privacy records current by adding a step to CI that reads your privacy manifest, validates it against the schema and the code it describes, and pushes it to wherever the records live — running on merge to the default branch, not on every pull request. The design decisions that matter are what to do when a field carrying personal data has no annotation (warn on the branch, block at merge is the usual balance), how to keep the sync idempotent so re-runs are safe, and how to fail loudly when the push breaks, because a sync that silently stops leaves a register that looks current and is not.
Where the step belongs
Two jobs, not one. On pull requests, validate: does the manifest parse, does it match the schema, and does it still describe the code in the diff? That check should be fast and should never write anything, because a branch is not the truth.
On merge to the default branch, push: send the validated manifest to wherever privacy records are maintained. This is the step that makes the register authoritative, and it should run after tests pass, in the same job that already has credentials to talk to your other services.
The reason to split them is contributor experience. A developer changing a schema should learn immediately that they have added personal data without classifying it. They should not be able to alter the organisation's record of processing from an unmerged branch.
What the push should contain
- The manifest itself — the declared personal data, classifications and the systems they belong to.
- The commit SHA, so a register entry can be traced back to the change that produced it.
- The repository and service identity, so the same organisation can ingest manifests from many codebases without them colliding.
- A timestamp from the pipeline rather than from the receiving system, so the record reflects when the code changed.
The unannotated-field policy
The hardest design decision is what to do when the scan finds a field that looks like personal data and carries no annotation. Three options, each with a real cost.
Warn only
Nothing blocks, a warning appears in the log. Frictionless, and almost universally ignored. Useful for the first few weeks while a codebase is being annotated; a permanent home for debt if it never ends.
Block on new, warn on existing
The practical middle. Existing unannotated fields are recorded as known debt and do not block; a newly introduced one fails the check. This stops the backlog growing while you work it down, and it is the policy most teams settle on.
Block on everything
Correct in principle, disruptive in practice unless the codebase is already fully annotated. Reasonable as a destination, rarely a good starting point.
Idempotency and replays
Your pipeline will re-run. Retries, reverted merges, replayed deployments and manual triggers all cause the same manifest to be pushed more than once. The sync therefore needs to be idempotent: pushing the same manifest twice should produce one register state, not two entries. Keying on repository plus service identity rather than on run identity is usually enough.
Reverts deserve specific thought. If a merge that added a personal data field is reverted, the next push should remove that field from the register — which means the push has to express the full current state of the manifest rather than a delta.
The failure mode that actually hurts
A pipeline that breaks loudly gets fixed. The dangerous failure is the quiet one: credentials expire, the sync step starts failing, the job is marked non-blocking so nobody notices, and the register keeps displaying the last successfully pushed state. It looks current. It is months old. And because it looks current, nobody goes looking.
Two defences. Alert on sync failure to the same place you alert on deployment failure — this is a compliance-affecting outage, not a nice-to-have. And surface a last-synced timestamp per service in the register itself, so a service that stopped reporting is visible to the person reading the record rather than only in a pipeline log.
Fitting it to an existing programme
None of this requires a new framework. It is the mechanism by which the Article 30 register you already owe stops being a document someone maintains and becomes an output of the systems it describes. The same pipeline hook can carry the evidence that supports your security controls, which is why teams usually add it once and use it for both.
If you want to see this wired into your own pipeline, you can explore it at noru.tech.