Completeness linting
This content is for 0.1. Switch to the latest version for up-to-date documentation.
effaced cannot find data you never declared — no library can. What it can do is point at every place such data could hide, so “we forgot to annotate the new column” becomes a build failure instead of a silent gap in your exports and erasures.
lint_completeness is the exact complement of
collect_data_map: every table in your metadata is either
in the data map, flagged whole (“this table carries no effaced
annotations”), or one of the effaced-owned effaced_* tables. Inside a
mapped table, every column is either annotated, a primary/foreign key, or
flagged. Nothing falls through.
from effaced import lint_completeness
for finding in lint_completeness(Base.metadata): print(finding.message)# table 'app_settings' carries no effaced annotations — its data is# invisible to export and erasure# column users.theme is not annotated and is neither a primary nor a# foreign keyA finding is a question, not a verdict: it marks data the manifest does not cover so a human can either annotate it or consciously exempt it. effaced never decides on your behalf that data is not personal — mechanisms, not determinations.
The CI gate
Section titled “The CI gate”Drop assert_data_map_complete into any test and the build fails the
moment a new table or column appears without an annotation:
from effaced.testing import assert_data_map_complete
def test_data_map_covers_the_schema() -> None: assert_data_map_complete( Base.metadata, exempt_tables=("app_settings",), # no personal data, judged so exempt_columns=("users.theme",), # a UI preference, not PII )Exemptions are conscious acknowledgements that live in your test code and
get reviewed like any other code — the judgement “this holds no personal
data” stays visible and stays yours. An exempt table silences all of its
findings; an exempt column ("table.column") silences just that field.
Stale exemptions fail too: if an exemption no longer matches any finding (the table was dropped, or finally annotated), the gate flags it for removal, so the list never outlives the schema it judged.
What is never flagged
Section titled “What is never flagged”- Primary and foreign keys — structural identity, covered by the subject graph rather than per-column annotations.
effaced_*tables — the audit, consent, and outbox tables mounted bybind_tablesare effaced-owned.
Everything else in a mapped table needs an annotation or an exemption.
The linter is read-only and needs no database connection — it walks the
same MetaData the collector does.