Validation Results¶
When a validation run completes, three types of output are produced:
- Findings -- individual issues, warnings, and info messages
- Artifacts -- files produced by validators (reports, logs, transformed data)
- Summaries -- aggregated counts for dashboards and long-term reporting
These all hang off the ValidationRun and ValidationStepRun models:
ValidationRun
├── findings (many ValidationFinding rows)
├── artifacts (many Artifact files)
├── step_runs (many ValidationStepRun)
│ └── findings (many ValidationFinding rows)
└── summary_record (one ValidationRunSummary)
└── step_summaries (many ValidationStepRunSummary)
Findings¶
A ValidationFinding is a single normalized issue discovered during validation. This is the primary unit of feedback that users see in the results table.
Fields¶
| Field | Type | Purpose |
|---|---|---|
validation_run |
FK(ValidationRun) | Parent run (denormalized for query performance) |
validation_step_run |
FK(ValidationStepRun) | The step execution that produced this finding |
ruleset_assertion |
FK(RulesetAssertion, nullable) | The assertion rule that triggered it, if applicable |
severity |
CharField | SUCCESS, INFO, WARNING, or ERROR |
code |
CharField | Machine-readable code (e.g. json.schema.required) |
message |
TextField | Human-readable description shown to the user |
path |
CharField | Location in the input data (JSON Pointer, XPath, or dotted path) |
meta |
JSONField | Optional extra metadata (line number, variable name, etc.) |
Findings are ordered by severity (ERROR first, then WARNING, INFO, SUCCESS) and then by creation date descending.
How findings are created¶
Validators don't write to the database directly. Instead, they emit ValidationIssue dataclass objects during execution. After a step finishes, the FindingsPersistence service normalizes these issues and bulk-creates ValidationFinding rows.
The flow:
Validator
→ emits ValidationIssue objects
→ collected in ValidationResult.issues
→ FindingsPersistence.persist_findings()
→ bulk-creates ValidationFinding rows
For advanced (async) validators like EnergyPlus, findings are produced in two stages. Input-stage findings (from assertions evaluated before the container runs) are persisted immediately. Output-stage findings (from the container's results and post-execution assertions) are appended when the callback arrives.
Severity levels¶
| Severity | Meaning | Effect on run status |
|---|---|---|
ERROR |
Blocking issue | Fails the step and the run |
WARNING |
Non-blocking issue that deserves review | Does not block |
INFO |
Informational message from the validator | Does not block |
SUCCESS |
Assertion passed (positive feedback) | Does not block |
SUCCESS findings are only created when an assertion passes and either the assertion has a custom success_message or the step has show_success_messages=True. See the assertions documentation for details.
Path field¶
The path field tells the user where in their input data the issue was found. The format depends on the validator:
- JSON Schema validators produce JSON Pointers (e.g.
/building/zones/0/name) - XML Schema validators produce XPaths
- CEL assertions produce dotted paths
For JSON submissions, a synthetic payload prefix is automatically stripped before storage so users see clean paths.
Artifacts¶
An Artifact is a file produced during a validation run. Common examples include EnergyPlus simulation output, transformed data files, and debug logs.
Fields¶
| Field | Type | Purpose |
|---|---|---|
org |
FK(Organization) | Owning organization |
validation_run |
FK(ValidationRun) | The run that produced this artifact |
label |
CharField | Human-readable name (e.g. "EnergyPlus Output") |
content_type |
CharField | MIME type (e.g. application/json) |
file |
FileField | The stored file |
size_bytes |
BigIntegerField | File size for display and quota tracking |
Artifact files are stored at artifacts/org-{org_id}/runs/{run_id}/{uuid}/{filename}.
Not all validators produce artifacts. Built-in validators (JSON Schema, XML Schema, CEL) typically produce only findings. Advanced validators running in containers (EnergyPlus, FMU) produce artifacts alongside their findings.
Summaries¶
Summaries are lightweight aggregate snapshots that persist even after findings and artifacts are purged by the retention policy. They power dashboards and long-term reporting.
ValidationRunSummary¶
One record per run with rolled-up counts:
| Field | Type | Purpose |
|---|---|---|
run |
OneToOne(ValidationRun) | The run being summarized |
status |
CharField | Final run status |
completed_at |
DateTimeField | When the run finished |
total_findings |
PositiveIntegerField | Total findings across all severities |
error_count |
PositiveIntegerField | ERROR findings |
warning_count |
PositiveIntegerField | WARNING findings |
info_count |
PositiveIntegerField | INFO findings |
assertion_total_count |
PositiveIntegerField | Total assertions evaluated |
assertion_failure_count |
PositiveIntegerField | Assertions that failed (ERROR severity only) |
extras |
JSONField | Optional metadata (e.g. exemplar messages for reporting) |
ValidationStepRunSummary¶
One record per step within a run summary:
| Field | Type | Purpose |
|---|---|---|
summary |
FK(ValidationRunSummary) | Parent summary |
step_run |
OneToOne(ValidationStepRun, nullable) | Can be NULL after cleanup |
step_name |
CharField | Denormalized step name |
step_order |
PositiveIntegerField | Denormalized step position |
status |
CharField | Step status (PASSED, FAILED, SKIPPED, etc.) |
error_count |
PositiveIntegerField | ERROR findings for this step |
warning_count |
PositiveIntegerField | WARNING findings for this step |
info_count |
PositiveIntegerField | INFO findings for this step |
Step names and order are denormalized into the summary so the data remains meaningful even if the workflow definition changes later.
Retention and cleanup¶
Each workflow defines two retention policies that control how long results are kept:
input_retention-- how long to keep user-submitted files after all consumers finish.output_retention-- how long to keep detailed output, including findings, artifacts, step input/output values, detailed errors, output envelopes/hashes, callback result locations, and evidence files.
Both policies default to DO_NOT_STORE; longer storage is an explicit author
choice. Output deadlines start at ValidationRun.ended_at, not launch time.
Finite output access is denied at the deadline even if physical deletion is
temporarily retrying.
purge_expired_outputs runs every five minutes. It deletes the complete
execution bundle and detailed output projections, but preserves the run's
terminal status, timing, aggregate summary counts, and purge timestamp so
dashboards and historical pass-rate charts continue to work. Unknown policies
fail closed to no retention, active runs are never eligible, missed terminal
signals are repaired by the sweep, and deletion failures remain eligible for
the next run.