PipeWhy Comparison Contract C6 version 1.0 October 30, 2026 PW-08.01 Comparison eligibility A comparison context records snapshot ID, source namespace/version, configured scope, full fingerprint context/algorithm ID, retained resolution and discovery completeness. Source versions intentionally differ across history. Namespace and configured scope must match before aligning numeric IDs. A namespace/scope mismatch returns a global INCOMPARABLE result with no claimed row matches. No name or path heuristic bridges different identity namespaces. Within the same namespace/scope, align persistent IDs first. The initial content profile requires the current C3 algorithm, a currently recognized strict/logical normalization profile, equal algorithm/profile/schema identities and equal ordering mode. Unknown algorithms and unrecognized profile identities are INCOMPARABLE, even when their declared IDs match each other. No automatic normalization/schema conversion is implemented. Incompatible content contexts make aligned rows INCOMPARABLE, including one-sided presence rows; v1 deliberately keeps this eligibility rule conservative. Ordered observations additionally require equal original start offsets; count/content differences at the same offset can be CHANGED. Context compatibility is explicit. Retained resolution ranks are dataset=1, record=2, value=3. Structural containers have level=0. Dataset summaries remain comparable when one snapshot retained finer detail. Finer historical selection requires both captures to retain that resolution; otherwise drill-down returns UNAVAILABLE with a capture-limit reason. Never read today's source to fabricate missing historical detail. compare() defaults to dataset summaries and records its requested resolution separately from each context's retained maximum. It excludes ordinary finer nodes before hierarchy aggregation. Nodes whose level changes across that selection boundary remain as explicit INCOMPARABLE evidence, so projection cannot hide them. PW-08.02 State truth table and evidence Stable row status codes are SAME=0, CHANGED=1, ADDED=2, REMOVED=3, UNKNOWN=4, INCOMPARABLE=5. Rows retain a primary reason code plus independent evidence flags. After namespace/scope checks, content incompatibility takes precedence. Otherwise: Both present, level differs: INCOMPARABLE (level/schema meaning changed). Both present, parent differs: CHANGED (observed containment change), retaining any read uncertainty separately. This reports a structural fact, not observed corruption. Both present, either read failed/missing/incomplete: UNKNOWN with explicit reason. Both present, fully captured compatible states equal: SAME under C3 limitations. Both present, fully captured compatible states differ: CHANGED. Only after present, both discovery inventories complete: ADDED. Only before present, both discovery inventories complete: REMOVED. Only one present, either discovery inventory incomplete: UNKNOWN. One-sided nodes finer than the other capture retained: UNKNOWN, never an inferred ADDED/REMOVED merely because the older capture omitted that resolution. Unseen artifacts never become REMOVED because of read/discovery failure. Absence means absence from the configured scope, not proof of physical deletion everywhere. Source capture failure strings are retained in the comparison context. They prevent an overall SAME scope even if every represented direct row is equal; unresolved failed or omitted observations remain explicit rather than disappearing. Structural containers have no direct fingerprint observation. With complete scoped discovery their own unchanged structural baseline can be derived from children. Hierarchy aggregation preserves own ADDED/REMOVED facts. Otherwise any known own or child change yields CHANGED; absent known change, INCOMPARABLE evidence dominates UNKNOWN, which dominates SAME. Direct missing observations on data nodes remain UNKNOWN even if children are equal. Containers with complete empty inventories can be SAME. Incomplete discovery prevents an unsupported empty-container SAME. Independent subtree flags retain change, unknown and incomparable evidence even when one primary parent status takes precedence. No parent SAME masks uncertain children. Derived parent changes are distinguished from direct observations. PW-08.03 Persistent identity, input layout and hierarchy IDs are stable non-sentinel uint64 keys within the recorded source namespace. They are not row positions, snapshot-local name IDs or hashes inferred from labels. The producer assigns stable unique keys to logical nodes at each retained level; record/value identities must not collide with dataset IDs. Artifact display labels are presentation metadata: a renamed label with the same stable ID does not create an ADDED/REMOVED pair or change encoded content by itself. An ArtifactObservations input borrows exact flat C6 columns: artifact_id Q, parent_id Q, level B, observation_id Q, observed_at_ns Q, observation_status B, read_complete B, fingerprint_offset Q, fingerprint_length Q; auxiliary fingerprint_words Q. All row columns have equal length. Q denotes uint64, B uint8. Inputs are one-dimensional contiguous native little-endian integer buffers following C1 lifetime rules; callers finish writes and keep aliases stable during use. No per-node Python graph or inferred object relationship is retained. Observation codes match C1: UNKNOWN=0, CAPTURED=1, ERROR=2. read_complete is 0/1; noncaptured rows cannot claim a complete read. Captured rows require a non-null observation ID and nonempty bounded fingerprint slice. ERROR rows retain a real observation ID; no-observation rows use NULL_ID and timestamp zero. Non-null observation IDs are unique within the input. Level-0 containers have no direct observation. Nodes cannot exceed the context's retained level. Known C3 states are validated through FingerprintState.from_words; unknown algorithms remain opaque. Input artifact IDs and observed containment links are acyclic; parent IDs outside the selected inventory are explicit scope boundaries, not invented rows. Alignment sorts index vectors and merges persistent IDs; original row order/labels are intact. Results are sorted by persistent ID, with before/after row indices or NULL_ID. The presentation forest uses after-parents for present-after nodes, retaining an edge only if its parent is present after. Removed nodes use their before-parent when present before. This avoids inventing a cycle from valid different historical trees. Raw before/after parent IDs remain evidence, including scope boundaries. Result rows use flat owned read-only columns: artifact_id Q, parent_id Q, before_parent_id Q, after_parent_id Q, before_index Q, after_index Q, before_observation_id Q, after_observation_id Q, before_observed_at_ns Q, after_observed_at_ns Q, level B, status B, reason B, flags Q. Snapshot/source/context references and discovery/retention limits accompany the arrays. Missing observation IDs and indices use NULL_ID; timestamp zero is unknown only when its observation ID is null. Exact stable values are published below and in tests/fixtures/comparison_schema_v1.json for query/analysis consumer validation. Discovery completeness covers the configured node inventory through the retained resolution. Partial inventories retain observed row facts but cannot prove absence. Drill-down filters only retained nodes in this forest at a requested finer level. Complete empty inventories can yield an available empty selection. An unseen finer selection under incomplete discovery is UNAVAILABLE, not an alleged empty result. Retrievable but incompatible rows keep INCOMPARABLE; retrieval does not convert them. PW-08.04 Schema and structural changes Adding/removing/retyping selected fields, changing source widths/exclusions or changing declared source-schema semantics requires a new schema identity and makes content INCOMPARABLE. No field is silently dropped, widened or converted. A field rename retains schema identity only under an explicit source policy declaring the name cosmetic; otherwise record a new schema identity. This does not override the user's strict typed-value normalization default. Stable IDs survive reordering and cosmetic artifact-label renames. Containment moves are reported as structural changes; node-level changes remain incomparable. Before/ after observation IDs/times and snapshot references support later evidence analysis. SAME remains probabilistic fingerprint evidence under C3, not a security guarantee, physical corruption diagnosis or permission to ignore incomplete source coverage. PW-08.16 Published result schema and consumer handoff Result schema_version is "1.0". Status values are listed in PW-08.02. Reason codes (uint8): 0 NONE 1 VALUE_CHANGED 2 PRESENCE_ADDED 3 PRESENCE_REMOVED 4 DISCOVERY_INCOMPLETE 5 NOT_CAPTURED 6 READ_ERROR 7 PARTIAL_READ 8 NAMESPACE_MISMATCH 9 SCOPE_MISMATCH 10 ALGORITHM_UNSUPPORTED 11 PROFILE_MISMATCH 12 SCHEMA_MISMATCH 13 MODE_MISMATCH 14 ORDERED_OFFSET_MISMATCH 15 PARENT_CHANGED 16 LEVEL_CHANGED 17 CHILD_CHANGED 18 CHILD_UNKNOWN 19 CHILD_INCOMPARABLE 20 RESOLUTION_UNAVAILABLE 21 CONTAINER_SAME 22 SELECTION_UNAVAILABLE 23 PROFILE_UNSUPPORTED 24 CAPTURE_INCOMPLETE Flags use uint64 bit masks; the following are bit POSITIONS, not mask values: 0 BEFORE_PRESENT 1 AFTER_PRESENT 2 BEFORE_DISCOVERY_COMPLETE 3 AFTER_DISCOVERY_COMPLETE 4 BEFORE_CAPTURED 5 AFTER_CAPTURED 6 BEFORE_READ_COMPLETE 7 AFTER_READ_COMPLETE 8 PARENT_CHANGED 9 LEVEL_CHANGED 10 SUBTREE_UNKNOWN 11 SUBTREE_INCOMPARABLE 12 SUBTREE_CHANGED 13 BEFORE_PARENT_OUTSIDE_SCOPE 14 AFTER_PARENT_OUTSIDE_SCOPE Consumers decode flags with bitwise AND; known change does not erase uncertainty. No child capture flag implies that its parent was directly captured. The one primary reason explains the selected row status; independent flags/context retain other facts. Overall status summarizes the selected scope and need not equal every row's status. Public API: pipewhy.compare exports ArtifactObservations, ComparisonContext, Status, Reason, Flags, compare, Availability and drill_down. Construct input views from the exact PW-08.03 columns and matching contexts. ComparisonContext.from_metadata accepts C5 metadata plus an explicit discovery_complete boolean; read completeness cannot substitute for discovery evidence. Generic C5 arrays have no inferred C6 role: producers must store/reload C6 column names, inventory completeness and metadata together and validate with ArtifactObservations. No live source reads occur here. compare(before, after, resolution="dataset") returns ComparisonResult with both contexts, overall status/reason, immutable columns and requested resolution. drill_down(before, after, artifact_id, resolution=...) returns DrillResult with both contexts, selected ID, resolution, availability/reason and a comparison or None. AVAILABLE means retained detail can be selected; its comparison may still be UNKNOWN or INCOMPARABLE. UNAVAILABLE always has comparison=None. Complete known empty detail can be AVAILABLE with no rows. Invalid inputs/resolutions raise explicit errors. Query/analysis consumer requirements: join on stable artifact_id within the recorded namespace/scope, preserve both snapshot IDs and observation links/times, expose uncertainty flags, distinguish direct and child-derived reasons, and keep retained maximum separate from requested resolution. In JSON use decimal strings for uint64 IDs/indices/timestamps/flags so browser number rounding cannot corrupt evidence. Do not infer causation, physical deletion or downstream corruption from C6 alone. Unknown future schema/status/reason semantics must fail closed at a consumer boundary. Qualification: 31 hand-authored truth-table cases and an independent numeric schema fixture constrain implementation and consumer projections. Separate integration tests carry C4 normalized values through C3, published/reopened C5 and C6 at two batch sizes for baseline/mutation/missing versions. These are reference consumer acceptance evidence; no independent human signoff or production collision approval is claimed. Native CPU/CUDA, query CLI and lineage analysis remain later work orders.