Snow Compliance DemoBoard review · v1

Approved 2026-10-16 · Full dataset

Full-dataset CUAD → Snowflake plan

Scale the demo from a 30-contract sample to all 510 CUAD contracts / 20,910 clause annotations, normalize them inside Snowflake with no API, generate the simulated RBAC + risk metadata, and put a Streamlit-in-Snowflake app in front of it.

510 contracts Normalized in SQL, no API 510 matters (1:1) Streamlit in Snowflake
Recommendation

Proceed in three gated phases, all inside Snowflake

The repository already contains the hard parts (DDL, access model, risk rubric, offline POC). What changes for the full dataset is scale and the requirement that the walkthrough be runnable by hand in Snowsight. The board approved the full-dataset scope and methodology on 2026-10-16.

510
contracts (30 today)
20,910
clause annotations (6,702 present)
~27 MB
full contract text
249 → 18
raw contract types → controlled vocabulary
  1. Parse & normalize inside Snowflake. Load the CUAD JSON into a VARIANT table, then flatten it with LATERAL FLATTEN in SQL. No external pipeline, no API.
  2. Generate simulated RBAC + risk metadata at scale. One matter per contract (510), SQL-generated synthetic layer, role buckets + matter_access_map, row access policy, masking, and the 9-rule simulated rubric.
  3. Web app on Streamlit in Snowflake. It runs as the caller's role, so RBAC + row access policies apply with no separate login store. External portal (with External OAuth) is a later step.
Today vs. target

Gap analysis: 30-doc demo to full dataset

AreaDemo coreFull datasetAction
Ingestionextract_cuad.py --limit 30510 docs, ~40 MB JSONAdd VARIANT load + SQL flatten
contract_type25 inferred types249 noisy stringsCORE.ref_contract_type map
Matter graph5 matters, 6 contracts each510 matters (1:1)Scale synthetic generator
RolesATTORNEY_0/1, PARALEGAL…same model, more bucketsAdd ATTORNEY_0/1/2
Full textsnippets only~27 MB availableStore full text
Risk register9 rules, 5 matters9 rules, 510 mattersRe-tune bands on distribution
Phase 1

Parse & normalize the full CUAD dataset

Snowflake caps a single VARIANT value at ~16 MB, and the master file is ~40 MB, so it is split into one document per line (NDJSON) for transport only — all parsing and normalization runs in SQL. The runbook has the copy-paste steps.

-- 1. stage the NDJSON and land it (one document per row)
COPY INTO RAW.raw_cuad_json (src, source_file)
FROM (SELECT $1, METADATA$FILENAME FROM @RAW.cuad_stage)
FILE_FORMAT = RAW.json_ff PATTERN = '.*cuad_docs.*';

-- 2. normalize in SQL with LATERAL FLATTEN
CREATE OR REPLACE TABLE RAW.raw_cuad_contracts AS
SELECT 'CUAD-' || LPAD(src:ord::string, 4, '0') AS contract_id,
       src:title::string                        AS contract_name,
       UPPER(TRIM(SPLIT_PART(src:title::string,'-',-1))) AS contract_type,
       src:paragraphs[0]:context::string        AS contract_text
FROM RAW.raw_cuad_json;
Data-quality gates 510 contracts · 20,910 annotations · 6,702 present · 41 categories · 0 orphaned clauses. All measured against the real CUADv1.json.

Assets: ingest/split_cuad_json.py (NDJSON split), sql/full_dataset/06_full_dataset.sql (consolidated DDL), and docs/SNOWFLAKE_WALKTHROUGH.md (the manual runbook).

Phase 2

Simulated RBAC + risk metadata

One matter per contract (510 matters). Everything synthetic is prefixed SYNTHETIC and generated deterministically in SQL — no external generator required.

ControlWhere
Access decision is data, not codeSECURITY.matter_access_map(role_name, matter_id, …)
Matter wallSECURITY.matter_access_policy attached to every matter_id object
PII protectionSECURITY.pii_mask + data_classification tags
Risk rubricCORE.ref_risk_rule → MARTS.mart_matter_risk (simulated)
Phase 3

Web application — Streamlit in Snowflake first

OptionAuth / loginGovernanceVerdict
Streamlit in SnowflakeSnowflake SSO/MFA; app runs as caller's roleInherits RBAC + row access + maskingRecommended first
External portalBuild login/SSO + session→role mappingDuplicates unless External OAuthLater, if a standalone portal is required
Snowpark Container Services / Native AppSnowflake authInheritsOverkill for the demo

Admin/auditability: native QUERY_HISTORY / ACCESS_HISTORY / LOGIN_HISTORY and POLICY_REFERENCES for data touches, plus an app-level audit table for UI actions mirroring poc/access.py.

Approved 2026-10-16

Board decisions

Approved The board approved the full-dataset scope and all six methodology decisions below.
DecisionResolution
Snowflake accountNew trial account (Enterprise)
Normalization locationIn-Snowflake VARIANT + SQL
Synthetic scale510 matters (1:1)
Full contract textStore full text (~27 MB)
Web app approachStreamlit in Snowflake first
Publish this roadmapThis page
Risk

Risks & mitigations

RiskImpactMitigation
Manual steps drift from sql/*.sqlMediumRunbook embeds the same DDL; keep files authoritative
contract_type noise (249 types)MediumControlled-vocabulary map; report unmapped as Other
Unstable contract_id across loadsMediumDerive from the NDJSON ordinal; document load order
Row-policy gaps via joinsHighAttach policy to every matter_id object; run the verification matrix
Demo mistaken for real client dataHighSYNTHETIC prefixes + CUAD CC BY 4.0 attribution + disclaimers
External app weakens governanceHighPrefer Streamlit in Snowflake; if external, mandate External OAuth + per-user role
Do it

Walk it through

The full step-by-step runbook (load → normalize → synthetic layer → RBAC → risk → verify → teardown) is docs/SNOWFLAKE_WALKTHROUGH.md, with the consolidated SQL in sql/full_dataset/06_full_dataset.sql. It needs only a Snowflake trial account and Snowsight — no API.

# transport shaping only (one JSON document per line)
python3 ingest/split_cuad_json.py --json CUADv1.json --out build/cuad_docs.jsonl
# then: upload to @RAW.cuad_stage and run sql/full_dataset/06_full_dataset.sql