The runner evaluates normalized suite core, plus normalized inventory when inventory-driven cases are present, and emits a machine-readable report.
This document defines the suite-runner report.
Product-certification reporting is specified separately in 60-product-certification.md.
The report is part of the platform contract, not an incidental output format.
The runner MUST determine:
- selected cases
- case execution order
- action events within each case
- artifact events within each case
- assertion outcomes within each case
- overall process exit status
Cases MUST be ordered deterministically by:
- suite item order in source-normalized order
- case-source-defined canonical case order
This ordering rule defines report order as well.
For inventory-driven items, canonical case order is canonical inventory order.
For workflow-driven items, canonical case order is the explicit deterministic order defined by the discovery source.
Every case record MUST contain a stable case identity.
At minimum that identity MUST include:
- suite item id
- case key
The report MUST NOT rely on test name alone as case identity.
If a case originates from inventory selection, the report MUST also emit the selected inventory test name explicitly.
Reports MAY contain both canonical and volatile fields.
Canonical fields are expected to be stable across equivalent runs. Volatile fields may vary even when the logical outcome is unchanged.
Canonical fields include:
- case identity
- case status
- assertion counts
- action kinds
- fail classifications
Volatile fields include, unless a stable mode overrides them:
- wall-clock timestamps
- elapsed durations
- host-specific execution metadata
Non-canonical analytics metadata is described separately in 41-analytics.md. Unless explicitly enabled, analytics metadata is outside the required canonical record set.
Derived analytical build packaging is described separately in 43-build-analytics.md. That package is downstream of the report and does not alter report semantics.
If volatile fields are emitted, they SHOULD be explicitly labeled or documented as non-canonical.
A structured line-oriented machine format is required for v0. JSONL is the canonical format.
JSON records MUST be UTF-8 encoded.
In golden mode, object keys MUST be emitted in ascending lexicographic order by raw UTF-8 byte sequence of the key strings.
A report MUST include:
- one header record
- one case summary record per case
- zero or more assertion records
- zero or more action records
- zero or more artifact records
- zero or more extraction records
- one final summary record
Optional non-canonical telemetry records may be emitted only through an explicit analytics lane as described in 41-analytics.md.
Human-facing console presentation is specified separately in 42-console-ui.md. That console is a presentation layer over the same report events, not a separate execution or reporting semantics.
Derived analytical packaging is specified separately in 43-build-analytics.md. That analytical package is a mechanical projection over report and telemetry records, not a second execution or reporting semantics.
The first record MUST be the header record:
{
"k": "observer_report",
"v": "0"
}Required canonical fields:
k: fixed stringobserver_reportv: spec version stringinventory_sha256: SHA-256 of normalized inventory bytessuite_sha256: SHA-256 of normalized suite bytesmode:defaultorgolden
The normalized byte sequences for those hash fields are defined in 25-normalization.md.
If a suite does not use inventory-driven case sources, inventory_sha256 MUST still be emitted as the SHA-256 of the canonical empty inventory byte sequence so report shape remains uniform.
Optional volatile fields:
generated_at_utchostimplementation
The canonical case identifier is a stable string derived from the tuple:
- suite item id
- case key
The derivation algorithm is:
- encode
item_idas UTF-8 bytes - append byte
0x1f - append UTF-8 bytes of
case_key - encode the resulting byte sequence using unpadded Base64URL
This string is emitted as case_id.
Reports MUST also emit item_id and case_key explicitly; consumers MUST NOT be required to reverse case_id to recover canonical identity.
For inventory-driven cases, reports MUST additionally emit test_name, where test_name = case_key.
Each finished case MUST emit one case summary record:
{
"k": "case",
"case_id": "...",
"item_id": "item-1",
"case_key": "Smoke::Version",
"test_name": "Smoke::Version",
"status": "pass"
}Required canonical fields:
k: fixed stringcasecase_iditem_idcase_keystatus:passorfailassert_passassert_failunhandled_action_fail
Required for inventory-driven cases only:
test_name
Optional canonical fields for workflow-oriented cases:
case_inputs: bounded canonical summary of discovered input fields
Optional volatile fields:
duration_msstarted_at_utcended_at_utc
Optional bounded diagnostic fields:
notes
Each evaluated assertion SHOULD emit one assertion record:
{
"k": "assert",
"case_id": "...",
"assert_ix": 0,
"status": "pass",
"msg": "expectation passed"
}Required canonical fields:
k: fixed stringassertcase_idassert_ix: zero-based assertion index within the casestatus:passorfailmsg
Optional fields:
loc: object withlineandcolpredicate
Each attempted action SHOULD emit one action record:
{
"k": "action",
"case_id": "...",
"action_ix": 0,
"action_id": "compile",
"action": "run",
"status": "ok"
}Required canonical fields:
k: fixed stringactioncase_idaction_ix: zero-based action index within the caseaction: stable action kind string such asrun,proc,httpGet,tcp,artifactPublish,artifactCheck,extractJson, orextractJsonlstatus:okorfailargs: bounded canonical action argument summary
Optional canonical fields:
action_id: stable action identifier within the suite item when one is declaredartifact_in: array of objects withnameandkindartifact_out: array of objects withnameandkind
Exactly one of these payloads MUST be present:
okfail
ok payload shapes:
- for
runandproc:exitout_lenerr_len- optional
out_preview_b64 - optional
err_preview_b64 - optional
out_truncated - optional
err_truncated
- for
httpGet:statusbody_len- optional
body_preview_b64 - optional
body_truncated
- for
tcp:bytes_len- optional
bytes_preview_b64 - optional
bytes_truncated
- for
artifactPublishandartifactCheck:namekindpath
- for
extractJsonandextractJsonl:source_artifactselectmatch_count- optional
value_text - optional
value_text_truncated
fail payload shape:
kindmsg- optional
code
Optional volatile fields:
duration_ms
Each explicit artifact publication or artifact check SHOULD emit one artifact record:
{
"k": "artifact",
"case_id": "...",
"artifact_ix": 0,
"action_ix": 0,
"name": "typed_unit",
"event": "publish",
"kind": "jsonl",
"status": "ok"
}Required canonical fields:
k: fixed stringartifactcase_idartifact_ix: zero-based artifact record index within the caseaction_ix: producing or checking action index within the casename: canonical artifact binding nameevent:publishorcheckkind: stable artifact kind stringstatus:okorfail
Optional canonical fields:
location: object with canonicalpathproducer_action_idconsumer_action_id
For publish events, location SHOULD be present.
For check events, the record MAY additionally include:
observed_exists: boolean encoded astrueorfalse
If status is fail, the record SHOULD include a canonical fail payload with stable kind and msg fields.
Each explicit structured extraction SHOULD emit one extraction record:
{
"k": "extract",
"case_id": "...",
"extract_ix": 0,
"action_ix": 1,
"source_artifact": "typed_unit",
"format": "jsonl",
"status": "ok"
}Required canonical fields:
k: fixed stringextractcase_idextract_ix: zero-based extraction index within the caseaction_ix: extraction action index within the casesource_artifact: artifact binding name consumed by the extractionformat: stable extraction format stringstatus:okorfail
Optional canonical fields:
select: declared selector stringsummary: bounded canonical extraction summary with:match_count- optional
value_text - optional
value_text_truncated
If status is fail, the record SHOULD include a canonical fail payload with stable kind and msg fields.
The last record MUST be a summary record:
{
"k": "summary",
"case_pass": 1,
"case_fail": 0,
"exit_code": 0
}Required canonical fields:
k: fixed stringsummarycase_passcase_failassert_passassert_failexit_code
The report is the authoritative structured fact stream for one completed build.
Downstream consumers MAY derive secondary artifacts from that stream, including:
- local console presentation
- telemetry summaries
- analytical build packages
- workflow artifact summaries
Those derived artifacts MUST be mechanically derived from explicit report and telemetry records.
They MUST NOT redefine:
- case identity
- action identity
- pass or fail semantics
- runner exit semantics
If a downstream analytical package is produced, the report remains the canonical source artifact and the analytical package remains derivative.
A case summary MUST contain:
- case id
- item id
- case key
- status
- assert pass count
- assert fail count
- unhandled action fail count
If the case is inventory-driven, it MUST also contain:
- test name
It MAY also contain:
- case input summary
- notes
- durations
- truncated previews
Reports MUST avoid dumping unbounded payloads by default.
Large output bodies SHOULD be represented by:
- total size
- explicit truncation markers
- bounded preview fields when needed
Preview fields that carry bytes MUST use unpadded Base64URL.
The runner MUST use this exit status model:
- 0: all cases passed
- 1: one or more cases failed
- 2: runner or specification error
If Observer is used in golden workflows, the implementation SHOULD provide a mode that excludes or normalizes volatile fields.
Golden mode does not change semantics. It changes serialization discipline so that logically equivalent runs are diff-stable.
Golden mode SHOULD omit volatile fields when feasible. If a volatile field cannot be omitted, it SHOULD be normalized to a stable sentinel value.