This document is a v0 proposal for multi-build analytical composition.
It is downstream of the single-build analytical package described in 43-build-analytics.md.
Single-build analytics explains one build as a knowledge artifact.
Multi-build analytics should explain change across builds.
Its purpose is to support:
- degradation detection
- metric drift analysis
- failure-shape comparison
- stability analysis by test identity
- lineage-aware exploration across parent, child, shard, and subbuild relationships
Multi-build composition is also purely derived.
It MUST NOT alter:
- the canonical report
- the single-build cube contract
- case identity
- action identity
- pass or fail semantics
The single-build cube remains the source analytical artifact.
The first multi-build stage SHOULD consume only previously derived cube artifacts plus optional explicit relationship metadata.
Representative inputs:
build-1234.cube.jsonbuild-1235.cube.jsonbuild-compare.meta.json
The Stage-2 design SHOULD NOT require re-reading raw report JSONL when cube artifacts are already available.
Representative command shape:
observer compare --cube build-1234.cube.json --cube build-1235.cube.json --out compare-1234-1235.json
Representative directory shape:
observer compare --cube-dir cubes/ --out compare-index.json
These command forms are future-facing Stage-2 sketches, not current implementation requirements.
An exact non-normative sample comparison artifact is provided in 92-build-compare-sample.json.
Exact non-normative sample source cubes for that pairwise artifact are provided in 102-build-compare-baseline-cube-sample.json and 103-build-compare-candidate-cube-sample.json.
An exact non-normative comparison-index sample artifact is provided in 99-build-compare-index-sample.json.
Exact non-normative sample source cubes for that comparison-index artifact are provided in 104-build-compare-index-build-1234-cube-sample.json, 105-build-compare-index-build-1235-cube-sample.json, and 106-build-compare-index-build-1236-cube-sample.json.
An invalid cube sample suitable for negative-path comparison testing is provided in 94-invalid-cube-sample.json.
Invalid comparison-metadata samples suitable for negative-path testing are provided in 96-invalid-compare-meta.json and 97-invalid-compare-relation-kind.json.
A valid comparison-metadata sample is provided in 98-build-compare-meta-sample.json.
Stage-2 comparison metadata is optional and purely relational.
It exists to supply explicit edges between builds when those edges are not already present in the comparison invocation context.
Representative metadata file shape:
{
"relations": [
{
"from_build_id": "build-1234",
"relation_kind": "previous",
"to_build_id": "build-1235"
}
]
}Required top-level field:
relations: array of relation rows
Required relation-row fields:
from_build_id: stringrelation_kind:previous,parent,child,shard, orsubbuildto_build_id: string
Unknown top-level fields SHOULD cause Stage-2 derivation failure so the metadata contract remains explicit.
The --cube-dir form is a distinct Stage-2 mode.
It SHOULD scan a directory of cube artifacts, derive a deterministic build set, apply explicit relation metadata when provided, and emit one comparison-index artifact.
Representative comparison-index artifact naming:
compare-index.json
The index artifact is not a pairwise delta artifact. It is an index over a build set plus any explicit pair links and relation edges.
The comparison-index explorer UX is described separately in 47-build-compare-index-explorer-ui.md.
The comparison stage should also carry explicit invalid fixtures.
Recommended invalid cases include:
- missing
build.build_idin one or more source cubes - duplicate
build_idvalues across input cubes when a comparison requires distinct builds - source cubes with unsupported
korvvalues - source cubes whose comparison anchors disagree structurally, such as missing
item_idortest_name - metric comparisons attempted across differing telemetry units
- relation metadata that references unknown builds
- relation metadata that uses unsupported
relation_kind - pairwise comparison invoked with fewer than two cubes
- pairwise comparison invoked with more than two cubes unless an explicit multi-build mode is selected
- directory mode invoked on an empty cube directory
- directory mode invoked on a directory containing duplicate
build_idvalues
The comparison command SHOULD emit one bounded machine-readable result payload on stdout.
Representative success payload:
{
"k": "observer_compare_result",
"v": "0",
"out": "compare-1234-1235.json",
"status": "ok",
"build_count": 2
}Required success fields:
k: fixed stringobserver_compare_resultv: fixed string0out: output comparison artifact path as supplied by the userstatus: fixed stringokbuild_count: non-negative integer
On failure, the command SHOULD emit one bounded machine-readable error payload on stderr.
Representative error payload:
{
"k": "observer_compare_error",
"v": "0",
"code": "unknown_relation_build",
"msg": "relation metadata references build id not present in input cubes",
"status": "error"
}Required error fields:
k: fixed stringobserver_compare_errorv: fixed string0code: stable error code stringmsg: human-readable error messagestatus: fixed stringerror
Recommended Stage-2 error codes include:
invalid_cube_shapeduplicate_build_idmissing_build_idinvalid_compare_metaunknown_relation_buildunsupported_relation_kindinvalid_compare_cardinalityempty_cube_dir
Directory mode SHOULD emit one bounded machine-readable result payload on stdout.
Representative success payload:
{
"k": "observer_compare_index_result",
"v": "0",
"out": "compare-index.json",
"status": "ok",
"build_count": 3,
"pair_count": 2
}Required fields:
k: fixed stringobserver_compare_index_resultv: fixed string0out: output comparison-index path as supplied by the userstatus: fixed stringokbuild_count: non-negative integerpair_count: non-negative integer
Pairwise and directory-mode outputs SHOULD also support static viewer emission.
Representative pairwise viewer command:
observer view --compare compare-1234-1235.json --out compare-1234-1235.view.html
Representative comparison-index viewer command:
observer view --compare-index compare-index.json --out compare-index.view.html
These viewer commands MUST read only the already-derived comparison artifact they are given.
They MUST NOT re-read raw reports or infer missing pair links.
Representative success payload:
{
"k": "observer_view_result",
"v": "0",
"out": "compare-1234-1235.view.html",
"view_kind": "compare",
"status": "ok"
}Representative comparison-index success payload:
{
"k": "observer_view_result",
"v": "0",
"out": "compare-index.view.html",
"view_kind": "compare_index",
"status": "ok"
}Exact non-normative HTML samples for the published Stage-2 artifacts are provided in 110-build-compare-view-sample.html and 111-build-compare-index-view-sample.html.
The Stage-2 output SHOULD be a structured comparison artifact rather than an overloaded single-build cube.
Representative top-level shape:
{
"k": "observer_build_compare",
"v": "0",
"builds": [ ... ],
"relations": [ ... ],
"comparisons": { ... },
"measures": { ... }
}Directory mode SHOULD emit a separate comparison-index artifact rather than forcing all builds into one pairwise delta shape.
Representative top-level index shape:
{
"k": "observer_build_compare_index",
"v": "0",
"builds": [ ... ],
"relations": [ ... ],
"pairs": [ ... ],
"measures": { ... }
}The v0 comparison artifact SHOULD use this top-level schema:
{
"k": "observer_build_compare",
"v": "0",
"builds": [ ... ],
"relations": [ ... ],
"comparisons": { ... },
"measures": { ... }
}Required top-level fields:
k: fixed stringobserver_build_comparev: fixed string0for v0builds: participating build identitiesrelations: explicit build-edge rows, possibly emptycomparisons: comparison tablesmeasures: aggregate comparison summaries
Each builds row SHOULD contain:
build_id: stringinventory_sha256: stringsuite_sha256: stringreport_mode:defaultorgoldenmeta: object carrying explicit source-cube metadata fields
Each relations row SHOULD contain:
from_build_id: stringrelation_kind: stringto_build_id: string
The row MUST reference build ids present in builds.
The comparisons object SHOULD contain these arrays:
case_status_changeaction_fail_changemetric_deltamissing_or_added_tests
The arrays MAY be empty, but they SHOULD always be present in Stage-2 pairwise artifacts.
Each case_status_change row SHOULD contain:
item_id: stringtest_name: stringbaseline_status:passorfailormissingcandidate_status:passorfailormissingcategory:pass_to_pass,pass_to_fail,fail_to_pass,fail_to_fail,added, orremoved
Each action_fail_change row SHOULD contain:
item_id: stringtest_name: stringaction: stringbaseline_fail: non-negative integercandidate_fail: non-negative integerdelta: signed integer
Each metric_delta row SHOULD contain:
item_id: stringtest_name: stringname: telemetry metric nameunit: telemetry metric unitbaseline_value: numericcandidate_value: numericdelta: numeric
Optional field:
delta_ratio: numeric when baseline is non-zero
Each missing_or_added_tests row SHOULD contain:
item_id: stringtest_name: stringcategory:addedorremoved
The measures object SHOULD contain:
build_count: non-negative integerpair_count: non-negative integerpass_to_fail: non-negative integerfail_to_pass: non-negative integeradded_tests: non-negative integerremoved_tests: non-negative integer
The comparison-index artifact SHOULD contain:
k: fixed stringobserver_build_compare_indexv: fixed string0builds: build entry rowsrelations: explicit relation rowspairs: explicit pair rowsmeasures: aggregate index summaries
Each pairs row SHOULD contain:
baseline_build_id: stringcandidate_build_id: stringcompare_artifact: string path or artifact identifier
Recommended measures fields for the index artifact:
build_countpair_countrelation_count
The builds array SHOULD list the participating build identities in deterministic order.
Recommended ordering:
- by explicit input order for direct pair comparison
- by
build_idlexicographic order for directory or index derivation when no stronger order is supplied
Each build entry SHOULD include:
build_idinventory_sha256suite_sha256report_mode- explicit external metadata fields already present in the source cube
Multi-build analytics should support explicit edges between builds.
Representative relation row fields:
from_build_idto_build_idrelation_kind
Permitted Stage-2 relation kinds may include:
previousparentchildshardsubbuild
These relations MUST be explicit. They MUST NOT be inferred heuristically from names.
All comparison logic should anchor on stable identities already preserved in the single-build cube:
build_iditem_idtest_namecase_idonly within one build- telemetry
name - telemetry
unit
For cross-build comparison, item_id plus test_name is the stable analytical key. case_id remains build-local.
Stage-2 SHOULD support these comparisons:
- case status change by
item_idandtest_name - assertion-failure change counts
- action failure-kind shifts
- telemetry metric deltas for scalar metrics with matching units
- telemetry distribution drift for vectors
Stage-2 SHOULD classify case movement explicitly.
Representative categories:
pass_to_passpass_to_failfail_to_passfail_to_failaddedremoved
These categories are mechanical derivations, not judgments.
Metric comparison MUST be explicit about units and comparability.
Rules:
- metrics compare only when names match exactly
- units compare only when units match exactly
- missing metric values remain missing; they MUST NOT be coerced to zero
- vector telemetry should not be flattened into scalar deltas in the normative artifact without an explicit summary rule
Representative scalar delta fields:
baseline_valuecandidate_valuedeltadelta_ratiowhen baseline is non-zero
The comparisons object SHOULD contain:
case_status_changeaction_fail_changemetric_deltamissing_or_added_tests
The measures object SHOULD contain:
build_countpair_countpass_to_failfail_to_passadded_testsremoved_tests
Multi-build derivation remains subject to the determinism gate.
That means:
- input cube order handling must be explicit
- relation edges must be explicit
- comparison keys must be explicit and stable
- output arrays must use deterministic ordering
- no inferred baseline or candidate choice is allowed without explicit rule
Recommended array ordering:
builds: as defined in section 7relations: byfrom_build_id, thenrelation_kind, thento_build_id- comparison rows: by
item_id, thentest_name, then comparison-specific secondary key
The static explorer in 44-build-explorer-ui.md may grow a Stage-2 comparison mode later.
That mode should add:
- baseline and candidate build selectors
- delta-focused scatter and heat-map views
- lineage navigation across explicit relation edges
But the Stage-1 single-build explorer should remain separate and simpler.
The current reference workflow now includes explicit HTML emission for:
- 92-build-compare-sample.json -> 110-build-compare-view-sample.html
- 99-build-compare-index-sample.json -> 111-build-compare-index-view-sample.html
The first multi-build analytical stage should compare a small explicit set of cube artifacts and emit one deterministic comparison artifact.
It should prioritize:
- exact identity alignment
- explicit relation edges
- deterministic delta rules
- drill-through to the underlying single-build facts