DataShield Ontology · documentation
Governance and Privacy
Measured against: /home/datashield/library at v0.22.188 (read-only pass, 2026-09-22). Audience: data-governance and privacy teams, developers, and AI agents driving the MCP surface. How to read this document: every claim carries a file:line citation into the tree it was measured from. Where the code and a stated objective disagree, the disagreement is written down rather than smoothed over (CLAUDE.md META-CALIBRATION 5). Normative keywords (MUST / SHOULD / MAY) are used per RFC 2119 only where the cited standard uses them.
On this page
- 1. The doctrine in one page — STANDARD-009
- 2. The kernel and its configuration
- 3. Masking
- 4. Sample values, statistics and acknowledgements
- 5. Stewardship — what a steward does
- 6. Master data — mdm and mdm_project
- 7. Evidence and fingerprints
- 8. Governance evidence harvested from providers
- 9. Pre-production checklist posture
- 10. Quick reference for agents
1. The doctrine in one page — STANDARD-009
The privacy doctrine lives in one document: docs/standards/STANDARD-009-privacy-classification-and-masking.md (status MANDATORY, :3). It governs what a column is (classification) and what the platform then does about it (governance, masking, refusal) (:33-38). It binds every producer and consumer of a column classification, every masking seam, the PHI egress gate, and every committed artifact that records a classification (:4-6, :42-59).
The fifteen rules (PC-1 … PC-15) in plain language:
| Rule | Plain-language meaning | Where it is measured |
|---|---|---|
| PC-1 One kernel, pure | Every classification decision comes from one function in lib/classification/. It never reads a database, file, clock, network, env var, or random source. Private "is this PII?" helpers elsewhere are forbidden and are deleted, not deprecated. | STANDARD-009:80-87; kernel entry lib/classification/classify.ts:73 (classifyColumn(e: ColumnEvidence)), :519 (classifyTable); gate audit:classification-kernel-purity |
| PC-2 One governance producer | Only deriveGovernance(decision, regime) produces the governance block (piiDetected, phiDetected, securityLevel, encryptionRequired, maskingRules, accessPolicy). Every field is derived through a config ladder, never typed literally at a call site. | STANDARD-009:89-95; lib/classification/governance.ts:46; ladder config/classification-evidence.config.ts:871 (GOVERNANCE_DERIVATION); gate audit:classification-governance-coherence |
PC-3 One pattern library, as data, joined on ClassId | The pattern corpus is configuration data, consumed by server lanes and the browser editor from the same artefact. Every join (mask token, severity, regulation, category) keys on ClassId, never on a display name — two distinct classes already shipped both displaying as "DEA Number". | STANDARD-009:97-105; config/classification-patterns.config.ts:77 (CLASSIFICATION_PATTERNS, 129 "id" rows measured; :5312 index CLASSIFICATION_PATTERN_BY_ID) |
PC-4 unclassified is not public | Five honest states: unscanned (we did not look), unclassified (we looked, nothing cleared its floor), candidate (plausible), classified, overridden. unclassified and candidate derive a non-PUBLIC floor (default INTERNAL). A surface that renders an unlabelled column as safe is non-conformant. | STANDARD-009:109-116; vocabulary table :63-72 |
| PC-5 Fail-closed readers | A consumer meeting an unknown class, unknown state, missing decision, stale classification_ref, or unavailable classifier must not serve raw values or publish. | STANDARD-009:118-124; lib/analytics/classificationRef.ts |
| PC-6 Value-free artifacts | Fixtures, corpora, baselines, probe captures, shadow diffs, impact ledgers, bug files, vet records and release notes must not contain a customer value, secret, credential or anything derived from one — shapes only. | STANDARD-009:128-139; lib/classification/valueLeak.ts; config/dataset-field-projection.config.ts:153 (FIELD_VALUE_BEARING_KEYS) |
| PC-7 Masking OFF by default; legally-required only | Operator rulings R2/R3 (2026-09-06) are reproduced verbatim and may not be re-opened. Posture default development (raw-by-default). Classification is still computed and listed regardless — visibility is not masking. Explicit requests and posture-independent surfaces (the mdm.golden_v1_* views) always mask. A caller-selectable masking framework may not appear on a read surface. No agent introduces a new privacy rule without an operator ruling. | STANDARD-009:143-170; config/masking-policy.config.ts:77-89 |
| PC-8 Error paths may not change the masking decision | Unknown-state policies are config-declared (RELOAD_MASKING_ON_UNKNOWN = keep_previous, QUERY_MASKING_ON_UNKNOWN = refuse) and disclosed. A catch that yields "unmasked" is forbidden; so is a fix that turns masking on where doctrine says off. | STANDARD-009:172-178; lib/query/maskingPolicy.ts |
| PC-9 Proposers propose, rules veto, humans on disagreement | See §1.1. | STANDARD-009:182-195. The policy is declared in the standard's own table; config/classification-arbitration.config.ts is named there but does not exist in the tree at v0.22.188, and its gate audit:classification-arbitration-integrity is planned — P4 (:320) |
| PC-10 Protective-only overrides | An override carries actor, reason and evidence legs and writes a ledger row. Relaxing a corroborated PII verdict is refused with a typed refusal, whoever asks. One reclassification verb. | STANDARD-009:197-202; gate audit:classification-decision-integrity planned — P3 (:321) |
| PC-11 Browser, session and model proposals never write directly | A payload from a browser engine, a session agent, or a model is never written verbatim to pii_class, semantic_type, governance, classification_state, has_pii, has_phi, compliance_level. | STANDARD-009:204-214 |
| PC-12 Inheritance is declared | A decision inherited through a derivation (join/union/transform) is declared as inherited, not assumed fresh. | STANDARD-009:216-225; live inheritance leg planned — P2 (:322) |
| PC-13 Append-only decisions, provenance mandatory | Every decision is a new row in an append-only ledger; the live column holds a pointer to the head that is gate-checked against the derived head. Every row carries source, tier, model/prompt version. | STANDARD-009:229-238; lib/analytics/evidenceLedger.ts. audit:audit-log-immutability gates the general audit log, not the classification decision ledger: the standard's own PC-13 gates (audit:column-writer-ownership, audit:classification-decision-integrity) are planned — P3 (:323) |
| PC-14 Measure before and after | No classification, masking or PHI-gate change ships without a before and an after measurement using the committed script. | STANDARD-009:242-266; audit:classification-baseline, audit:classification-shadow-diff, audit:classification-impact-ledger |
| PC-15 The doctrine is gated or it is theatre | A change to classification, masking or the PHI gate updates this standard, docs/codebase/ and the release notes in the same change, and every rule above names the gate that checks it. A rule with no gate is recorded as planned and may not be described anywhere as enforced. Doc claims about this seam are themselves checkable facts. | STANDARD-009:270-277; enforcement table :307-328 |
Which of these are actually enforced: STANDARD-009:298-328 is the authority, and its own status vocabulary (:300-305) distinguishes chained (a release:gates link today) from unchained (exists, run deliberately) from planned (does not exist yet). PC-1, PC-2 (legs 1–3), PC-3, PC-5's golden-boundary leg, PC-6's residue gate and PC-14's regression/quality gates are chained; the PC-4/PC-5 fail-closed gate, the PC-9/PC-11 arbitration gate, the PC-10 override-direction gate, the PC-12 live inheritance leg and the PC-13 writer-ownership gates do not exist yet. Nothing in that table may be read as enforced until it is chained.
1.1 Who decides (PC-9, A1–A8)
STANDARD-009:186-195 declares the arbitration policy as a table; the operational meaning:
- The rule floor vetoes. A proposal failing gates G1–G7 is suppressed with a disclosed reason — "a veto a vote could reverse is not a veto" (A1). The kernel comment at
lib/classification/classify.ts:195names the forbidden case: a vote that movesunclassifiedtoclassifiedby removing a deterministic suppression. - Votes are over the vocabulary bucket (
pii|secret|format,config/conduit-analysis.config.ts:273PATTERN_CATEGORY_VOCABULARY), never the pattern id; the id is chosen by the specificity lattice (lib/classification/lattice.ts) (A2). - Abstention is not dissent — an engine outage cannot change a classification (A3).
- Disagreement produces
candidateplus a steward-queue entry. That is the only point where a human appears; silent tie-breaks are forbidden (A4), and the interim tie-break is the more protective bucket, with the dissent recorded verbatim (A5). - Every proposal, including losers, is recorded with tier and model/prompt version (A7); the governance bucket is derived from the id, never from a proposer's self-report (A8 — measured contradicting its own id 3 times in 40).
2. The kernel and its configuration
| Module | Role (measured) |
|---|---|
lib/classification/classify.ts | 522 lines. classifyColumn(e: ColumnEvidence): ClassificationDecision (:73); candidate set built from regex hits, name-lexicon rules, secret rules, custom detectors and generalizer hints (:113-124); per-class floors resolve to classified / candidate / below-floor (:12). classifyTable (:519). |
lib/classification/governance.ts:46 | deriveGovernance(decision, regime) — the single PC-2 producer. |
lib/classification/frameworks.ts | obligationsFor(classId) — the ONE reader of the compliance-obligation table (config/classification-frameworks.config.ts:24). |
lib/classification/index.ts, patterns.ts, lattice.ts, regexSafety.ts, valueLeak.ts, shadowSink.ts, digest.ts, wire.ts, types.ts, collect/, signals/, text/ | Pattern access, specificity lattice, regex-safety gate, value-leak detector (PC-6), shadow-run sink (PC-14), evidence digesting, wire shapes. |
2.1 Compliance frameworks and obligationsFor(classId)
config/classification-frameworks.config.ts declares seven frameworks (COMPLIANCE_FRAMEWORKS, :52-59) with edition, jurisdiction and a citation URL — five active and two charter. A charter row is a crosswalk placeholder and MUST carry zero obligations; an active row MUST carry at least one (:22-23, validator :209):
| id | Framework | Edition cited | Status | Legacy tags absorbed |
|---|---|---|---|---|
hipaa | HIPAA Privacy Rule — Safe Harbor de-identification | 45 CFR §164.514 (2013 Omnibus) | active | HIPAA |
gdpr | General Data Protection Regulation | Regulation (EU) 2016/679 | active | GDPR, UK-GDPR |
pci_dss | PCI Data Security Standard | v4.0.1 (2024) | active | PCI-DSS, PCI |
soc2 | AICPA SOC 2 Trust Services Criteria | TSC 2017 (2022 revised points of focus) | active | SOC2 |
fedramp | FedRAMP Moderate baseline | Rev. 5 (2023) | active | NIST |
iso27701 | ISO/IEC 27701 Privacy Information Management | 2019 | charter | ISO27001 |
nist_800_53 | NIST SP 800-53 Security and Privacy Controls | Rev. 5 (2020) | charter | — |
Obligations are rows joined on ClassId (COMPLIANCE_OBLIGATIONS, :108-163): HIPAA Safe-Harbor letters A–Q are attached to the kernel's hipaa_* classes across 21 rows (e.g. hipaa_email → letter F, :115; hipaa_ssn_dashed/hipaa_ssn_nodash → G, :116-117); letter R, the statute's catch-all "any other unique identifying number", has no kernel class and so no row. One builder per active framework — HIPAA Safe Harbor, GDPR personal-data, PCI PAN, SOC 2 credential, FedRAMP PII (:78-102) — emits 49 obligations in total. Obligation text is bounded to 240 characters and must carry an RFC 2119 keyword (:74-76). The compliance[] free strings that predate this table (PCI-DSS vs PCI) are recorded as the disagreement this file resolved (:8).
Count discipline (a live disagreement, verified twice): STANDARD-009 :72 warns that the 18 statutory HIPAA identifiers and the number of hipaa_* pattern rows are different numbers and must not be conflated, and puts the pattern-row figure at 10 after the Safe-Harbor taxonomy split (restated at :276). The live config does not reproduce it: grep -c '"id": "hipaa_' config/classification-patterns.config.ts measures 40 rows, of which 12 carry the PHI category and 33 carry PII_QUASI_IDENTIFIER — so 10 is not the count of hipaa_-id rows, nor of PHI-category rows, under any reading. Treat the live config as authoritative and the standard's figure as a stale snapshot; PC-15 (:270-277) makes the mismatch a defect of the standard, not a cosmetic one.
2.2 The regex-safety gate
config/classification-regex-safety.config.ts makes every kernel regex answer to a structural bound and an adversarial time budget (:7):
REGEX_TIME_BUDGET = { perInputMs: 25, samples: 3, warmupRuns: 1, reportSlowest: 8 }(:66), against anADVERSARIAL_CORPUS(:83) over 4 KiB inputs.- Discovery roots
["lib/classification", "config"](:236); carrier exclusions declared as data (:170). - A grandfathered-findings ledger (
:208) capped atMAX_GRANDFATHERED_FINDINGS = 4(:219); four entries today (:209-216), each with a prepared rewrite (REGEX_SAFETY_PREPARED_REWRITES,:228): twonested_unbounded_quantifier—hipaa_udiandhipaa_name_labeled, both timed linear (:209-212) — and twotime_budget—hipaa_email(measured 29 ms against the 25 ms budget) andemail_standard(37 ms) (:213-216). The ceiling is exact, not slack: the config's own validator refuses aMAX_GRANDFATHERED_FINDINGSabove the row count and tells the committer to lower it when an entry retires (:275-276). The gate isaudit:classification-regex-safety(inpackage.json), backed bylib/classification/__tests__/regexSafety.test.ts(:223).
2.3 A naming disagreement to know about
The MCP umbrella named classification is not the PII kernel. Its published description (docs/versions/v0.22.188/tools/classification.md:14-16) is "Product/industry classification catalog lookup + AI dataset tagging with a human-review workflow" — six commands: systems_list, lookup (UNSPSC-style codes), dataset_classify, batch_classify, proposals_list, proposal_decide (:18-23). It shares the STANDARD-009 posture that proposals never auto-apply (batch_classify "never auto-applies"; proposal_decide is the only way a proposal becomes live), but its subject is product/industry taxonomy tags. The PII/PHI kernel is reached through the catalog and loader seams (lib/data_catalog/piiLabel.ts, lib/query/loader.ts), not through a same-named umbrella. Agents searching for "classification" should read both.
3. Masking
3.1 Posture
config/masking-policy.config.ts is "the privacy-posture knob" (:4). Two postures (:19-26): development (DEFAULT — the loader applies no auto-masking at any compliance level) and protected (auto-mask per reconciled authority). MASKING_POLICY_CONFIG.posture = "development" (:89), typed as the union so === "protected" comparisons still type-check (:87). Three things are posture-independent by design (:43-69): the MDM read boundary (the inner compliance boundary, always_protected, :50-54), at-rest write maskers (a value captured under development must not become exposable by a later flip, :59-61 — lib/mdm/goldenRecordMasking.ts maskFreeTextPiiAlways), and any explicit per-dataset masking_mode. The documented consequence — one golden served raw by the read boundary while the golden_v1_* view masks it, under development — is stated so it "can never again be read as a defect" (:64-69).
3.2 masking_set and the masked-view family
- Changing a dataset's masking mode is routed through
dataset_admin.masking_set(change mode) anddataset_admin.masking_reload(lib/query/maskingPolicy.ts:144-145,MASKING_CHANGE_ROUTE/MASKING_APPLY_ROUTE); applying at load isdataset_ingest.loadwithmasking_mode.masking_set's confirm is "the human ack", while destruction happens in the loader (lib/query/loader.ts:122). - Masked golden consumption reads the
mdm.golden_v1_<category>views (lib/mdm/goldenConsumptionRead.ts:10;stewardship.golden_read), already-masked at the read boundary (lib/mdm/goldenRagRender.ts:11). - Exported masked views are rendered by ONE projection writer with SQL dialects as data (
config/masked-view-dialects.config.ts:2-27,MASKED_VIEW_DIALECTS:99,DEFAULT_PARTIAL_KEEP_LAST = 4:36); DuckDB is the materialisation dialect (:133, LIB-BUG-1412). Mask generators themselves (MASK_SQL,goldenRecordMasking.maskAttributeValue,mdm.golden_mask_scalar,tokenizer.GENERALIZERS) are out of STANDARD-009's scope by design (STANDARD-009:36-38).
3.3 generalization — privacy-preserving column transforms
Umbrella generalization (7 commands: list, apply, preview, update, delete, bulk_apply, stats; docs/versions/v0.22.188/tools/generalization.md:14). View-mode (reversible masking at query/prompt egress) works; physical in-place transform is not implemented and returns not_implemented (FEAT-033) (:16). config/generalization.config.ts declares a strategy↔column domain-fit preflight (domainFitMinHitRate, :29-44, LIB-BUG-482) and a per-regime strategy map (COMPLIANCE_STRATEGY_MAP, :96-101): hipaa → dob_generalize, zip_generalize, age_band; gdpr → email_domain, phone_partial; ccpa → ssn_partial, email_domain; pci → empty, deliberately — no built-in strategy addresses PAN (:93).
4. Sample values, statistics and acknowledgements
4.1 The sample-value ladder
config/data-catalog.config.ts defines SAMPLE_VALUE_POLICIES = ["plain", "masked", "omit"] (:1110) and SAMPLE_VALUE_POLICY with default: "omit" (:1116). The prior default was plain (operator ruling 2026-09-06, :1088); plain remains available but requiresAcknowledgement: ["plain"] (:1133) — a caller must pass acknowledge_plain_samples: true, and the acknowledgement is written to the job record (:1106). masked is shapeOnly (:1158); omit is the withheldPolicy (:1146). The ladder's rungs are properties of a catalog.* asset: column / scope / provider / platform (config/dataset-field-projection.config.ts:22), with scope outranking provider (config/data-catalog.config.ts:1123).
config/dataset-field-projection.config.ts closes the read side: the registry detail read had bypassed the ladder (:16); it now resolves the same ladder (no second ladder, no new rule — :11-13) and, when it withholds, writes a sample_values_withheld: { reason, policy } marker (SAMPLE_WITHHELD_KEY, :107) citing GDPR Art. 5(2) accountability (:55). Reasons are a closed vocabulary of five (:87-102): audience_not_entitled (the audience floor — a public audience never receives values), sample_policy_omit, plain_samples_not_acknowledged, sample_policy_masked, sample_policy_plain. Value-bearing keys are absent, not nulled, so the marker is not an existence probe for anonymous callers (:59-61).
4.2 Export statistics and sidecars
lib/catalog/exportCatalog.ts writes sidecars into the customer's destination bucket beside the data (config/catalog-export-statistics.config.ts:5): catalog/_metadata/governance.json carries "PII exposure, masking rules, framework applicability" (exportCatalog.ts:9) and has exactly one writer (:134). Sidecar format versions (config/catalog-export-statistics.config.ts:75-88): dataset 1.0, schema 1.1, lineage 1.0, quality 1.1, governance 1.3 — 1.3 added data_file, the first governance claim about the data artefact itself rather than about the generated masked view (LIB-BUG-1412), after LIB-BUG-1394 found a sidecar claiming masking no artefact applied (:22).
EXPORT_STATISTICS_POLICIES = ["omit", "aggregate_only", "plain"] (:29); statistics_policy is stamped on each document (:62-70). lib/catalog/statisticsPolicy.ts resolves the policy (resolveExportStatisticsPolicy, :81) and throws ExportStatisticsAcknowledgementError when a plain-persisting policy is requested without acknowledgePlainStatistics: true (:90-91); the value-exposure predicate is shared with the catalog ladder (policyPersistsPlainValues, :26) so this module "contributes no policy vocabulary" (:14).
4.3 The acknowledgements audit
audit:catalog-plain-sample-sweep (scripts/audit/catalog-plain-sample-sweep.ts, LIB-BUG-1041 item 3) reports every row still at plain. It is read-only by default: an earlier version healed legacy acknowledgements forward into the immutable ledger as a side effect of reading; the heal is now opt-in (--heal) and announced in output (:5-9). Sibling gates measured in package.json: audit:sample-store-integrity, audit:classification-config-agreement, audit:classification-row-predicate, audit:classification-single-seam, audit:governance-vocab-integrity, audit:catalog-classification-quality, audit:classification-coverage-floor, audit:classification-ootb, audit:classification-golden-file, audit:classification-golden-bakeoff, audit:classification-regression.
5. Stewardship — what a steward does
5.1 The MCP surface
Umbrella stewardship (29 commands, docs/versions/v0.22.188/tools/stewardship.md:14-16): worklists (worklist_get), supervised proposals (proposal_get, proposal_decide), pattern trust (trust_status), notification config, audit_queue, disposition rules (rule_enact / rule_release / rule_suspend / rule_resume / rule_retire / rule_backfill / rule_status), masked golden consumption (golden_read, golden_events), quality KPIs (quality_recovered, quality_status, quality_refresh), predictive recommendations (recommendations, recommendation_summary, recommendation_decide, recommendation_efficacy, recommendation_refresh), and dashboard reads (flow_summary, subflow_summary, workbench, alerts_queue, impact_preview). Golden changes go through one decision writer (lib/mdm/stewardshipDecisionWriter.ts) and surface an events envelope {emitted, visible_after, read_with} — an empty read before visible_after is expected lag, not a lost event (:20).
5.2 The UI
app/app/stewardship/page.tsx mounts StewardshipDashboard.tsx. Slots (app/app/stewardship/slots/): HeaderBand, TabsRail, FilterBar, StatTiles, StewardCards, FlowSankey (the Sankey flow), RecoveryTrend, AlertList, RecordsTable, WhyColumnMenu, GovernancePopout, Chat, ReportsPage, SubmapHeader, PopupHost. Popups (popups/): Workbench, RecordDetailDrawer, CompareRecordsTable, LineageWorkspace, SourcesPopup, StagePopup, StepDrillPopup, StepLensPopup, RibbonPopover. A steward therefore: sees the flow and its bottlenecks (age buckets are ISO 8601 durations from config/stewardship-aging.config.ts:18,62-86), opens a queue item, compares candidate records, reads lineage, decides (approve / reject / reopen / assign / escalate / undo — lib/mdm/stewardshipUndo.ts), previews blast radius (lib/mdm/stewardshipImpactPreview.ts), and enacts disposition rules that dispose of future items automatically. Chat grounding and governance help are config-driven (config/stewardship-chat-grounding.config.ts, config/stewardship-help.config.ts) and surface-tested (__tests__/chat-grounding.surface.test.tsx, governance-help.surface.test.tsx).
6. Master data — mdm and mdm_project
6.1 mdm
Published purpose (docs/versions/v0.22.188/tools/mdm.md:14-17): golden entities, source links, relationships; a stewardship review queue with survivorship-based golden materialisation; match-config authoring (config_create / config_trial / match_explain / config_train / battery_run / config_promote / config_demote) with an absolute-identifier tier that routes identifier conflicts to review instead of blind-merging; normalization and enrichment registries; curated reference sets; parent-scoped hierarchies; governance overrides and confirmed goldens; an open entity-type vocabulary (entity_type_list / entity_type_get / entity_type_create / entity_type_retire) with per-type matching_mode. Resolution runs are resolution_run; goldens are read via entity_get / golden_explain and masked via the read boundary (§3.1).
Key configuration:
| Concern | Config | Measured detail |
|---|---|---|
| Blocking + scoring | config/mdm-resolution.config.ts | chunked blocking-key buckets with a memory bound (:39-57, LIB-BUG-592); cross-parent threshold min(match_threshold + delta, 1) (:69-76); sentinel-fraction backstop against constant collapse — the case that "mega-merged 286 people into one" (:412-415); mistyped safety controls can never fail open as always-blocking (:195) |
| Enrichers | config/mdm-enricher-kinds.config.ts | stages pre_match / post_golden / post_master (:40); kinds regex, builtin, reference_lookup, sql_lookup, rest_lookup, classification_lookup (:71-138); DB CHECK ≡ matrix ≡ dispatch enforced by a release gate (:13) |
| Consent, forbid-clearing, authz | config/mdm-consent.config.ts, config/mdm-forbid-clearing.config.ts, config/mdm-enricher-authz.config.ts | consent fixture domain seeded by scripts/mdm/seed-consent-fixture-domain.ts |
| Stewardship, quality, validation | config/mdm-stewardship.config.ts, mdm-quality.config.ts, mdm-validation.config.ts | — |
Known limitation — LIB-BUG-1450 (open): a match configuration with empty blocking rules succeeds rather than being refused; recorded as hardening owed in docs/audit/catalog-elt-3-BOOT-PROMPT.md:53 alongside 1448 (tests excluded from typecheck), 1449 (duplicate detector blind to lowercase) and an identity-mask quality note (^\d+ regexp on city/state columns). Until it lands, a config_trial / resolution_run over a config with no blocking keys should be treated by governance reviewers as unbounded pairwise comparison, not as a validated model.
6.2 mdm_project
Guided onboarding by interview (15 commands, docs/versions/v0.22.188/tools/mdm_project.md:14-16): project_start → interview_next / interview_answer / interview_skip → project_status / systems_map_get / project_brief → spec_generate → spec_get / decision_answer / group_accept / gate_status → model_generate / generation_status; project_abandon is terminal. config/mdm-project.config.ts holds the interview bank (INTERVIEW_QUESTION_BANK, :119), system roles source_of_record / contributing_source / destination (:54), and an entity_types question (:165). The privacy-relevant question is the exclusion list: a field the business names is dropped from the matching model — no comparison, no blocking key — "so a sensitive field is never used to decide that two records are the same, even internally"; display masking is unaffected because it is governed elsewhere (:195-203).
7. Evidence and fingerprints
evidence(4 commands:show,verify,list,determine;docs/versions/v0.22.188/tools/evidence.md:14-16): content-hashed aggregate proofs behind analytics claims, written atstory.scan, each pinning its snapshot in the retention index and hashed over RFC 8785 canonical JSON.lib/analytics/evidenceLedger.ts,lib/analytics/classificationRef.ts(binds a determination to the classification in force — PC-5). Limits inconfig/evidence.config.ts:6-9(listDefaultLimit 20,listMaxLimit 100).fingerprint(6 commands:detect,list,get,validate,create,delete;tools/fingerprint.md:14-16): source-system fingerprints (Salesforce, SAP, Workday, …) — 95 built-in plus custom, counted fromBUILT_INand exported asBUILT_IN_COUNT(lib/server/sourceFingerprints.ts:65-680), so the published figure is derived rather than typed. Detection keys mainly on column-name shape (columnExacts/columnPatterns), but it is not value-free: definitions may carryvaluePatternswith aminMatchRatio(:50, e.g. the Salesforce 15/18-char id andstagenamevocabulary at:74), anddetectacceptssample_values— so adetectcall over real rows passes customer values to the matcher. Bounds and tiers are config (config/fingerprint.config.ts:17, generic tokens:129, command auth tiers:154). Governance use: knowing a table is a Salesforce Opportunity export changes which classes are plausible before any value is read.
8. Governance evidence harvested from providers
config/governance-control.config.ts declares, per provider kind, which native governance surfaces are real evidence, which are not a surface of that kind, and why — so an absent label is never read as "nothing is sensitive". Capability vocabulary GOVERNANCE_CAPABILITIES (:67-86) includes sensitivity_label (a vendor's own taxonomy), policy_tag, security_label, dynamic_masking, field_encryption, column_comment, field_security.
| Provider | Measured posture (GOVERNANCE_CONTROL, :244+) |
|---|---|
| SQL Server | sensitivity_label real: sys.sensitivity_classifications label, information type and rank, each under its own tag namespace (:328-332); DDM real: sys.masked_columns is a per-column binding, so the masking function token (partial, email, default) is collectable evidence (:340) |
| BigQuery | policy_tag is the governed taxonomy whose bindings are the access control (:222); masking is a data policy bound to a policy tag, so no separate per-column DDM surface is read (:426) |
| MySQL Community 8.4 | no sensitivity catalog, no catalog-level dynamic masking (:376, :392) — declared absent, not approximated |
| Snowflake | schema-inventory masking policies are OBJECTS, not per-column bindings, and are declared under masking_policy (:224) |
| Salesforce | the field-classification gap: Salesforce ships SecurityClassification, ComplianceGroup, DataOwner, DataSensitivityLevel on FieldDefinition, reachable only via the Tooling API or SOQL, both refused by this kind's metadata-only allowlist; "its absence here MUST NOT be read as 'the org classified nothing'" — closing it needs a second transport with its own value-freedom argument and is recorded as owed (:560). Write is unsupported (Metadata API only, :568). Shield Platform Encryption is the one field_encryption surface (:86, :227). |
row_level_security is its own declared capability (:83), not a synonym for field_security or security_label: it is real on SQL Server (:346-348, captured by lib/data_catalog/harvest/sqlserver.ts), and on BigQuery (:428), Databricks (:475) and PostgreSQL (:522) it is declared readable but unread by this release's harvest — an honest "not looked at", distinct from the NOT_A_SURFACE_OF declarations that cover tag / policy_tag / security_label for the SaaS and generic-SQL kinds (:234, :322). Harvested tags are mapped to kernel classes by PROVIDER_TAG_CLASS_MAP rules and enter the kernel as evidence — a proposer, never a direct writer (PC-11). Snowflake SYSTEM$CLASSIFY opt-in is an open operator decision, "as proposer only" (docs/audit/catalog-elt-3-BOOT-PROMPT.md:56).
9. Pre-production checklist posture
docs/audit/pre-production-checklist.md (31 lines) is the single place to re-raise security-hygiene items the operator ruled to defer until cutover; they "should not be re-raised as standing 'owed' items before then" (:3-6). Listed, without urging action:
| # | Item | Id | Recorded step |
|---|---|---|---|
| 1 | Redact 4 plaintext ai.oauth_token rows in registry.app_settings_history | Decision 18 | under the STANDARD-007 lock, --plan then npm run ops:redact-app-settings-history -- --apply --operator-authorised (:12) |
| 2 | Rotate the AI OAuth token | Action 19 / LIB-BUG-1392 (P1, open) | requires the vault-backing bundle first (:13) |
| 3 | Re-store the Snowflake key-pair credential for connection f45c3042… | Action 13 | fixture credential for acceptance, not a production rotation (:14) |
| 4 | Out-of-schedule pdl-billing-reconcile run | LIB-BUG-1369 / Decision 21 | operator-attended, quiet hour (:15) |
| 5 | Build the FEAT-027 admin credential vault | FEAT-027 (not built) | registry.connections.config_encrypted is the only credential store; the vault umbrella is the PII detokenization vault (:16) |
Warn-mode gates to flip at cutover (:20-23): audit:consumer-env-currency; audit:lineage-emitter-enrolment (chain-linked from v0.22.188 as release:gates link 26; measured 1 of 11 declared writers enrolled, so the platform openly declares lineage_pending). Each row is to be re-verified against current state before actioning (:27-31).
10. Quick reference for agents
| Want to… | Use | Constraint |
|---|---|---|
| Know what a column is | catalog field projection / data_catalog reads | states per PC-4; unclassified ≠ public; withheld samples carry sample_values_withheld |
| Change masking on a dataset | dataset_admin.masking_set → masking_reload | posture is config; never expose a framework on a read (PC-7) |
| See plain sample values | pass acknowledge_plain_samples: true | recorded on the job (config/data-catalog.config.ts:1106) |
| Export with plain statistics | statistics_policy: "plain" + acknowledgePlainStatistics: true | otherwise ExportStatisticsAcknowledgementError |
| Review a proposal | stewardship.proposal_decide, classification.proposal_decide | proposals never auto-apply (PC-11) |
| Read a golden | stewardship.golden_read (masked view) | always masked regardless of posture |
| Propose or override a class | no such verb is on the MCP surface at v0.22.188 — proposals enter the kernel as evidence (PC-11) | PC-10's "exactly ONE reclassification verb", protective-only, is doctrine awaiting its gate (audit:classification-decision-integrity, planned — P3, STANDARD-009:321) |
| Add a privacy rule or threshold | do not — operator ruling required | STANDARD-009:169-170 |
Measured against the release named in the text. Raw Markdown: /ontology/documentation/governance-privacy.md. This page is also served as raw Markdown at the same URL with a .md suffix, for agents and search tools that prefer plain text. Every capability statement cites the source file and line it was measured from.
You've seen the proof
Ready for a number? Scope your deployment and we'll price it against your own economics.
Get your quote →