PipeWhy Array Contract C1 version 0.1
PW-02.01-.04: initial CPU schema decisions, October 30, 2026

Current CUDA addendum: PW-10's CV1.1 implementation and real Windows GPU
qualification are specified in CUDA_CONTRACT.txt and CUDA_ACCEPTANCE.txt.
The historical CPU sections below do not themselves claim CUDA behavior. CV1
provides qualified integer borrowing/producer synchronization; fingerprint APIs
explicitly transfer host C2 batches and retain owners through completion/cleanup.
Borrowing a device view is not a claim of device C2 fingerprint execution.

Persistent artifact identity and scan-local row indexes are distinct namespaces.
Persistent identities are assigned in the source namespace and retained in a
dictionary; dense local indexes accelerate computation but must not be compared
across scans. A hash is never sufficient by itself to collapse two source identities.
Source identity allocation and string interning implementation remain later work.

ID/reference/offset columns use uint64. UINT64_MAX is the absent-reference sentinel;
valid IDs and local indexes are 0 through UINT64_MAX-1. Counts/offset arithmetic
must use checked bounds before allocation or conversion; no silent wrap. Payload
values can use signed/unsigned 8,16,32,64-bit integers according to type semantics.
Payload uint64 max is a valid value: reference sentinel rules apply to reference
columns only. Timestamp, decimal and float equivalence remain normalization policy.

Serialized integer storage is little-endian. CPU views currently accept native
integer buffers on qualified little-endian hosts; non-native formats require an
explicit normalization copy and are rejected by this borrowing interface. CUDA
residency and synchronization require a separate qualified implementation.

Validity uses flat uint64 words, least-significant bit first. Logical row n uses
word n//64, bit n%64; 1 means present and 0 means null. The final word's unused high
bits must be zero. Empty input uses zero bitmap words. Missing versus null is
separate status/type evidence, not an overloaded ID value. Bitmap validation is
still to be implemented with the table schema.

IntegerView borrows a one-dimensional, C-contiguous integer buffer and retains its
owner through Python's memoryview. It exposes logical length, dtype, read-only flag
and the view itself; it does not serialize addresses or transfer memory. Mutable
inputs may still change through another alias; snapshot coordination must enforce
stable source ownership while kernels read. Read-only is a buffer capability, not
a promise that all external aliases are immutable. Callers must not release a view
while another consumer uses it. No arbitrary raw pointer constructor is provided.
PW-02.05: Specify artifact columns
Artifact table (schema 1.0)
Row columns: artifact_id:uint64, parent_id:uint64, kind_id:uint8, name_id:uint64.
artifact_id is unique and cannot equal UINT64_MAX. parent_id is either an
existing artifact_id or UINT64_MAX for a root. Self-parenting and containment
cycles are invalid. name_id resolves in the name dictionary; kind_id is an opaque
adapter-defined code whose dictionary/version is supplied by the scan context.
All four columns have exactly the same length, including zero. No physical row
ordering carries identity. Names do not have to be globally unique.

PW-02.06: Specify value and payload columns
Value table (schema 1.0)
Row columns: value_id:uint64, artifact_id:uint64, field_id:uint64, type_id:uint8,
payload_offset:uint64, payload_length:uint64.
Auxiliary arrays: payload_words:uint64, validity_bitmap:uint64.
value_id is unique/non-sentinel; artifact_id resolves in the artifact table;
field_id and type_id resolve under the normalization schema. Offset and length
are measured in uint64 words, not bytes; offset <= word count and length <=
word count-offset. Empty payloads may start at the end. Overlapping slices are
permitted for deduplication. A null row must have zero payload length; its offset
must still be within bounds. Bitmap length is ceil(row_count/64), with zero tail
padding. Empty and null are distinguished through validity, not payload length.

PW-02.07: Specify observation columns
Observation table (schema 1.0)
Row columns: observation_id:uint64, artifact_id:uint64, snapshot_id:uint64,
observed_at_ns:uint64, status_id:uint8, policy_id:uint64, algorithm_id:uint64,
fingerprint_offset:uint64, fingerprint_length:uint64.
Auxiliary array: fingerprint_words:uint64. IDs are unique/non-sentinel only in
the observation_id column; all references resolve in their named context.
observed_at_ns is nonnegative Unix UTC nanoseconds for observation time, not
source-event time. Status codes: 0 UNKNOWN, 1 CAPTURED, 2 ERROR. CAPTURED requires
at least one fingerprint word; UNKNOWN/ERROR require zero words. Slice bounds
are checked as for values. Fingerprint width is algorithm-version metadata,
not a hardcoded two-lane security claim. Offsets retain flat variable-length
states until PW-04 specifies an algorithm's exact lane count and combine rules.

PW-02.08: Specify lineage edge columns
Lineage edge table (schema 1.0)
Row columns: edge_id:uint64, source_id:uint64, target_id:uint64,
relation_id:uint8, evidence_id:uint64, transform_id:uint64.
edge_id is unique/non-sentinel. Both endpoints resolve in the artifact table.
Relation codes: 0 COPY, 1 TRANSFORM, 2 DERIVED. Evidence resolves in the evidence
context. COPY requires transform_id=UINT64_MAX; TRANSFORM requires a valid
transform reference; DERIVED may carry either a declared transform or sentinel.
Lineage may contain cycles and self-edges; it is distinct from the acyclic
artifact containment hierarchy. Edge semantics do not prove equality/causation.

PW-02.09: Specify incident columns
Incident table (schema 1.0)
Row columns: incident_id:uint64, artifact_id:uint64, observation_id:uint64,
reason_id:uint8, evidence_id:uint64, grouping_policy_id:uint64.
incident_id is unique/non-sentinel. artifact_id and observation_id resolve in
the respective tables, and the observation must refer to that same artifact.
Evidence and grouping policy references resolve in supplied contexts. reason_id
is a versioned analysis-policy code, not a global corruption assertion. All row
columns have equal length. Counts and recurring grouping remain PW-09 behavior.

PW-02.10: Define schema compatibility
Schema versions and compatibility
C1 schema 1.0 names/dtypes/units above are authoritative for initial tables.
Readers require an exact version match until an explicit migration exists.
Adding/removing/renaming columns, changing widths/sentinels/units or enum meaning
is incompatible; do not silently reinterpret. Additive descriptive metadata can
be compatible only when it cannot change logical interpretation. Schema version,
normalization policy version and fingerprint algorithm version are independent.
Unknown columns are rejected at this boundary rather than accidentally ignored.
Version checks precede buffer inspection. Evidence/policy/snapshot/transform/name
references require supplied valid-ID contexts, never guessed from integer ranges.

PW-02.11: Specify logical and physical buffer descriptors
Buffer descriptor (C7, CPU implementation and CUDA boundary)
A descriptor has an optional logical_id (uint64 excluding UINT64_MAX), dtype,
logical length, device kind, device ID when meaningful, read-only capability,
access/alignment contract, and an owner-retaining buffer handle. Anonymous input
views have logical_id=None; identity is never derived from a pointer. CPU views
have device='cpu', device_id=None. The current CPU implementation exposes a
memoryview handle and byte-safe scalar access; it does not publish an unverified
raw address or imply an aligned SIMD load. Shape is strictly one-dimensional.
CUDA descriptors must retain the exporting allocation, device ID and stream/event
ownership. CUDA views cannot be passed to the CPU borrowing constructor; an
explicit adapter/backend qualification is required in PW-10.

PW-02.12: Define borrowing, ownership and invalidation
Buffer ownership and lifetime
IntegerView borrows storage without copying and retains an independent memoryview
export, keeping the original owner alive. Releasing a caller's original memoryview
does not release this export. The caller must keep aliases stable while a scan
reads and must not release the exposed IntegerView.buffer during consumer use.
The view descriptor is read-only; underlying writable payloads remain writable.
Explicit release invalidates buffer operations with an error; it never returns
empty data as a substitute. Owned normalized/transfer buffers use the same owner
retention rule but their allocation/copy is explicit in the normalization/backend
operation. Snapshot publication may not persist a transient pointer as identity.

PW-02.13: Define synchronization and CPU alignment rules
Synchronization and alignment
CPU scalar views read through Python's byte-safe memoryview interface. Unaligned
native integer buffers are therefore permitted for this interface, without any
claim of SIMD/CUDA alignment. A backend needing stronger alignment must explicitly
validate its own handle or normalize to an owned aligned allocation; it cannot
assume alignment from dtype alone. Noncontiguous/multidimensional/foreign-endian
or noninteger inputs require explicit normalization and are rejected here.
The caller must finish writes and prevent concurrent mutation before a CPU scan.
A future CUDA adapter must record producer stream/event and device identity,
wait for producer completion before reads, keep allocation live until consumer
completion, and synchronize explicitly before CPU observation or ownership release.
Cross-device transfers must be explicit. No stream ownership or asynchronous GPU
completion is inferred from the existence of a pointer. PW-10 verifies this on
real supported hardware; no CUDA runtime behavior is claimed by this specification.
