Machine-Readable Suite Results
date: 2026/09/25
Contributors:
Xylar Asay-Davis
Claude
Summary
polaris serial reports a suite’s results only as text: a line per task
on stdout and a POLARIS TASK: PASS or FAIL line in each task’s log in
case_outputs. The nightly CDash job scrapes those strings and reports an
execution time of 1.0 for every task because the real times are printed
and then discarded (see
#771).
This design adds a JSON results file that polaris serial writes alongside
the suite’s pickle file. It records each task’s outcome, elapsed time and
baseline differences, plus the provenance of the run. Downstream tools read
the file instead of the logs, so the log wording is no longer an interface.
Success means that the CDash job can build its test report from this file alone, with real per-task times, including for a run that was killed before it finished.
Requirements
Requirement: A suite run leaves a machine-readable record of its results.
For each task, the record gives its outcome, the outcome of any baseline comparison, its elapsed time, the steps it ran and where its log is. For the run, it gives the Polaris version, machine, compiler and build.
Requirement: The record is useful when the run does not finish.
A job that hits its wall-clock limit still leaves a record of the tasks that finished and of those that did not run.
Requirement: The record carries what the text output discards.
This includes elapsed times and the norms of any baseline differences.
Requirement: Consumers can detect a change in the format.
Algorithm Design
Algorithm Design: A suite run leaves a machine-readable record of its results.
polaris serial <suite> writes <suite>_results.json in the suite’s base
work directory. polaris serial in a task’s work directory writes
task_results.json there. In both cases, the provenance comes from the
provenance file in the base work directory.
The file is a JSON object:
{
"schema_version": 1,
"suite": "omega_pr",
"complete": true,
"start_time": "2026-09-25T10:15:02-05:00",
"elapsed_seconds": 1834.2,
"summary": {"total": 12, "passed": 11, "failed": 1, "pending": 0},
"provenance": {
"polaris_version": "1.1.0-alpha.6",
"polaris_git_version": "4b2347b9e",
"component_git_version": "a1b2c3d",
"machine": "chrysalis",
"partition": "compute",
"compiler": "intel",
"build_directory": "/path/to/build",
"build_type": "Release",
"work_directory": "/path/to/work",
"baseline_work_directory": "/path/to/baseline"
},
"tasks": [
{
"path": "ocean/planar/manufactured_solution/convergence_both/default",
"status": "fail",
"execution": "pass",
"baseline": "fail",
"elapsed_seconds": 95.3,
"steps_to_run": ["init", "forward"],
"log": "case_outputs/ocean_planar_manufactured_solution_...log",
"baseline_diffs": {
"normalVelocity": {"l1": 1.2e-9, "l2": 3.4e-10, "linf": 5.6e-11}
}
}
]
}
tasks lists every task in the suite in the order they run.
statusispass,failorpending. A task passes if it ran without error and no baseline comparison failed, the same criterion as the text output.executionispassorfail, andnullfor a pending task.baselineispassorfail, andnullif no comparison was made.logis relative to the directory holding the results file. It isnullfor a single task, whose steps log separately.A provenance value that the
provenancefile does not record isnull.
Algorithm Design: The record is useful when the run does not finish.
The file is written before the first task runs, with every task pending,
and rewritten after each task finishes. complete is false until the last
write, which happens before polaris serial exits with a failure status.
Each write goes to a temporary file that is renamed into place, so a reader
never sees a partial file.
A consumer that finds complete: false after the job has ended reports the
pending tasks as not run.
Algorithm Design: The record carries what the text output discards.
elapsed_seconds is wall-clock time, for each task and for the suite so
far. baseline_diffs holds the maximum l1, l2 and l_infinity norms of the
differences for each compared variable, merged across a task’s steps in the
same way as for <suite>_output_for_pr.md. It is empty when no comparison
was made. Non-finite norms are written as the strings NaN, Infinity and
-Infinity so that the file is strict JSON.
Algorithm Design: Consumers can detect a change in the format.
schema_version is an integer. It is incremented when a field is removed
or its meaning changes, not when a field is added.
Implementation
Implementation: A suite run leaves a machine-readable record of its results.
A new module, polaris.run.results, holds a TaskResult record and the
function that writes the file. polaris.run.serial.run_tasks() builds a
TaskResult per task, replacing the tuple that _log_and_run_task()
returns now. It reads provenance with the parser it already uses for
<suite>_output_for_pr.md, but from the base work directory, so that a
single task has provenance as well.
The POLARIS TASK and POLARIS BASELINE log lines and
<suite>_output_for_pr.md are unchanged.
Implementation: The record is useful when the run does not finish.
run_tasks() calls the writer once before the loop over tasks, once after
each task and once with complete=True before logging the task runtimes,
which is where a failed suite exits.
Testing
Testing and Validation: A suite run leaves a machine-readable record of its results.
Unit tests run run_tasks() on a mocked suite with a task that passes, one
that raises, and one whose baseline comparison fails, and check the
contents of the file. A run of omega_pr against a baseline from main
checks the file from a real suite.
Testing and Validation: The record is useful when the run does not finish.
A unit test checks the file after the first of several tasks, and that the final file is written when the suite fails.