Versioned domain contracts
Representation
Every record is a frozen, slotted dataclass with schema_version: 1. Nested records
are immutable; collections are tuples in Python and arrays in JSON. Field annotations
and dataclass metadata are the authoritative structural contracts used by the strict
decoder and schema generator. Semantic relationships are checked by constructors.
Wire names are snake_case. Handoff examples describe product information; the
generated schemas define the concrete cross-language representation. All wire
fields, including nullable fields and constructor defaults, must be present. Missing
or unknown fields, unsupported versions, duplicate JSON keys, malformed enums,
floating-point numbers, NaN/infinity, boolean-as-integer and unpaired Unicode
surrogates are rejected. JSON payloads are bounded to 8 MiB; individual strings to
one million Unicode scalar values. The API milestone must add lower per-operation
transport limits and abuse controls appropriate to each use case.
Timestamps are UTC Unix epoch milliseconds, named *_at_ms or *_until_ms.
Counts, timestamps and revisions use integers in 0..9007199254740991, so later
JavaScript clients can represent them exactly. Normalized scores/confidence use
basis points (0..10000) except UserForecast.confidence, which follows the
handoff's 0..100 scale. A YES forecast at 70 confidence means 70% probability of
YES; a NO forecast at 70 means 30% probability of YES. Confidence is informational.
Reputation separates binary resolved forecasts from INVALID forecasts. Accuracy
uses floor(correct * 10000 / binary_resolved); absent observations use null,
not an invented zero or perfect score. Brier, calibration, consistency and creator
quality fields are versioned measurement snapshots. Their aggregation and update
algorithms belong to the reputation application service, which must exclude
INVALID outcomes from binary scoring and deduplicate finalized event processing.
Structural versus semantic validation
Generated files in schemas/v1/ use JSON Schema Draft 2020-12 and forbid additional
properties. Each schema is standalone, with local $defs; consumers need not
fetch remote references. IDs under https://schemas.forecast.network/v1/ are stable
identifiers, not an assertion that a hosting service has been deployed.
JSON Schema expresses field shape and bounds. It does not prove cross-record identity, causal timing, exact hashes, state-dependent requirements, provider independence or the relationship between metric counts and ratios. All writes and persisted snapshots must pass the domain constructor/decoder as well. Consumers must not treat a schema-only pass as permission to finalize or publish.
Use to_dict, from_dict, dumps and loads from
forecast_domain.serialization. Persist the complete validated representation.
schema_for and all_record_types expose structural schema generation; normal
application code must not execute a user-supplied schema or arbitrary Python type.
Canonical commitments: profile v1
This is a narrowly specified application profile, not RFC 8785/JCS.
- Convert records to objects containing every field, including
schema_version; enums to strings; tuples to arrays. Preserve array order. - Allow null, booleans, Unicode scalar strings, safe integers, arrays, and objects with string keys. Generic hash inputs may use signed safe integers; the domain records constrain numeric fields to nonnegative values. Reject floats and bytes.
- Sort object keys lexicographically by Unicode scalar values, recursively. This differs from JavaScript's default UTF-16 sort for some non-BMP keys.
- Encode compact JSON as UTF-8 without a BOM or trailing newline. Use
,and:separators, no whitespace. Emit non-ASCII characters literally, do not escape/, use\"and\\for quote/backslash, short escapes for backspace, form feed, newline, carriage return and tab, and lowercase\u00xxfor other controls. Do not normalize Unicode; composed and decomposed strings are distinct content. - Prepend the exact ASCII bytes
forecast-network:sha256:canonical-json:v1\n(the final\nis one LF byte), then compute SHA-256. Render 64 lowercase hex characters. The domain separator is not part of the JSON wire payload.
Golden vectors live in tests/fixtures/commitments-v1.json and are verified by
tests/test_contracts.py; they include Unicode, controls, numeric-looking keys,
safe-integer bounds, nested objects and key ordering. Future Rust/TypeScript
implementations must match these vectors before writing commitments.
Raw captured evidence bytes have an ordinary SHA-256 digest, identified as
content_sha256 and urn:sha256:<digest>. A domain evidence record's commitment
uses the versioned profile above and also binds source, capture metadata and
provenance. These digests serve distinct purposes and must not be interchanged.
Immutability and trust
Publication binds the complete specification to its successful assessment. Later commands cannot edit it. Source policies and clause identifiers are part of that commitment. Resolution artifacts bind the exact forecast/specification, retained evidence snapshots, source verification and decision provenance.
A URL is a locator, not evidence content. Adapters must fetch and hash bytes, persist an immutable snapshot, verify source ownership/reliability, and only then submit its record. Deleted or changed live pages do not alter captured evidence. The domain checks integrity relationships but cannot establish what bytes a remote server served or whether an official source told the truth.
AI provenance records task, provider, model and model version, policy version, schema version, input/output commitments and decision time. Trusted adapters must authenticate these attestations and preserve the associated versioned outputs. Output commitments bind the exact validation flags, source verification, proposed outcome/criteria/confidence, counter-judge agreement and dispute disposition. A user can supply captured dispute evidence without an AI collector attestation; the later independent review must explicitly verify or dismiss that evidence. INVALID_EVIDENCE is an audited disposition, never an implicit successful validation. Public hashes are not signatures: anyone can recompute a hash over false data. Provider identifiers must be normalized to a trusted registry so aliases cannot bypass independent-review requirements.
Breaking changes require an explicit new schema/profile version and migration decision. Do not change committed v1 interpretation in place once external systems consume it. Contract generation refuses to silently delete obsolete schema files.