Prove Enforcement with the Regression Pack
ContractEnforcementRule gates every Spark write against a contract before it executes: a
write that violates its contract is aborted, and no output is ever created. The
regression pack re-runs that guarantee as a script instead of a one-off manual check, with
real spark-submit invocations and real assertions — not mocks.
Run it
Section titled “Run it”./dev/regressionRequires a working ./dev/test environment (Codespaces already provides one).
./dev/regression-dockerBuilds a self-contained image and runs the same pack inside it — useful for verifying the guarantee on a machine that hasn’t set up JDK/sbt/Spark at all. Only Docker is required.
What it checks
Section titled “What it checks”Ten real cases, exiting non-zero if any behaves unexpectedly — schema-level
enforcement, then the same pass/fail proof for static data-quality
verification, for input/output type
role-consistency, for a job that
checkpoints between reading and writing, and
for per-output input scoping (derivedFrom):
-
Contract satisfied (
demo/contracts/invaract_output.yaml) — the job exits0, the report saysPASS, and the output file exists. -
Contract violated (
demo/contracts/invaract_output_broken_example.yaml, which requires acustomer_namecolumn the demo transformation never produces) — the job exits non-zero, the report saysFAILwith aMISSING_OUTPUT_FIELDviolation, and — the core guarantee — the output file is never created. -
Data-quality constraint satisfied (
demo/contracts/invaract_output_data_quality_pass.yaml, declaringvalue_tiermust be one of[high, low]) — the demo transformation’s ownCASE WHEN value > 50 THEN 'high' ELSE 'low' ENDprovably satisfies that, so the job exits0and the publishedContractValidationEventcarries aGuaranteedverdict forvalue_tier. -
Data-quality constraint violated (
demo/contracts/invaract_output_data_quality_fail.yaml, declaringvalue_tiermust be[high]only) — the same transformation provably produces'low'on itsELSEbranch, so the job exits non-zero, the report shows aDATA_QUALITY_VIOLATION, and the output file is never created. -
Declared type consistent with observed usage (
demo/contracts/invaract_output_role_consistency_pass.yaml, declaring theordersinputDATA_ASSET) —orders’valuecolumn genuinely does derivevalue_squared/value_tierin the produced output, so the job exits0and the publishedContractValidationEventcarries aConformsverdict fororders. -
Declared type contradicted by observed usage (
demo/contracts/invaract_output_role_consistency_fail.yaml, declaring the sameordersinputCONTROL) — its data still reaches a produced output column, the exact high-confidence contradiction role-consistency checking exists to catch, so the job exits non-zero, the report shows aROLE_CONSISTENCY_VIOLATION, and the output file is never created. -
Checkpointed job, contract satisfied (
demo/contracts/invaract_output.yaml, the same contract as case 1, run with the demo harness’s--checkpointflag, which.checkpoint()s the input right after reading it) — the job really does checkpoint (Spark’s checkpoint directory exists), yet the declared input is found: it exits0and the publishedContractValidationEventreportsPASSEDwith no unverifiable inputs. -
Checkpointed job, declared input never read (
demo/contracts/invaract_output_checkpoint_missing_input.yaml, which also declares anorders_typoinput the job never reads) — a checkpoint must not hide that: the job exits non-zero, the report showsMISSING_INPUTfordemo/input/sample_typo.csv, the event’sunverifiableInputsis empty (the input was proven missing, not merely unverifiable), and the output file is never created. -
derivedFrom, a write that reads only its own inputs (demo/contracts/invaract_output_derived_from.yaml, two outputs and three inputs;orders_reportisderivedFrom: [orders]) — the job writesorders_reportand reads onlyorders, so it exits0even thoughcustomersandproductsare declared and never read: they are only expected of the other output. -
derivedFrom, a write that reads an input outside its output’sderivedFrom(the same contract, the same job, now landing oncatalog_report, which isderivedFrom: [customers, products]) — the job still readsorders, an input the contract declares but not as a source of this output, so it exits non-zero, the report showsUNDECLARED_INPUT, and the output file is never created.
Cases 3/4 attach spark.invaract.staticDataQuality=true purely via --conf, the same
way a platform team would against a job whose source they don’t own — see Verify Static
Data Quality. Cases 5/6 attach
spark.invaract.roleConsistency=true the identical way — see Declaring Input and Output
Types. Cases 9/10 attach
spark.invaract.rejectUndeclaredInputs=true the same way — see Map Outputs to Their
Inputs.
Why this exists as a separate script
Section titled “Why this exists as a separate script”Unit tests can assert that ContractEnforcementRule.verifyOrThrow throws for a given
input, but that doesn’t prove the real Spark write never happens end-to-end, via a real
spark-submit process, the way a production job would run. The regression pack closes
that gap: it’s the same proof Your First Contract
walked through manually, automated and asserted on every run.