F1: RecordUnappliedEntry keeps the primary's wire entry on the in-memory log entry (internal Relayed) and ToLogEntryMessage returns it through the same normalisation the store uses, so a replica with no store no longer relays a skipped put as a delete; memory and disk relay identically. Soul's 4d probes are committed (chained replica keeps the row, key-prefixed reads serve only owned keys, primary-all serves everything; the two measurements are explicit) with a memory/disk relay-parity test. Rust: captured-bytes cases for unowned_schema and variant_unsupported (with and without a subscription). The error-code set is stated as a closed enum in the schema description (both copies) and in cultnet-distributed-database.md: adding a code is one coordinated change across runtimes, since Rust and TypeScript refuse an unknown code. The Soul 4c probe file's header says what it guards. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
46 lines
2.8 KiB
JSON
46 lines
2.8 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://github.com/GameCult/CultLib/contracts/cultnet/cultnet.error.schema.json",
|
|
"title": "CultNet Error Message",
|
|
"description": "R-X (docs/cultnet-selection-cut.md, fix batch 4): CultNetErrorMessage is a MessagePack-CSharp map-mode object, so every declared key is written on the wire, nil included - routingHint, code and details are `required` here the same as cultnet.raw-document-header.schema.json's nullable fields, not optional just because a given refusal leaves them null.",
|
|
"type": "object",
|
|
"required": [
|
|
"schemaVersion",
|
|
"error",
|
|
"routingHint",
|
|
"code",
|
|
"details"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"schemaVersion": { "const": "cultnet.error.v0" },
|
|
"error": { "type": "string", "minLength": 1 },
|
|
"routingHint": {
|
|
"description": "Optional shard routing detail for an authority failure. Null for every selection/cursor refusal code (R-N).",
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"required": ["shard", "reason"],
|
|
"properties": {
|
|
"shard": { "type": ["object", "null"] },
|
|
"reason": { "type": "string" }
|
|
}
|
|
},
|
|
"code": {
|
|
"description": "Machine-readable refusal code (R-N). Additive: a v0 peer that does not know this field ignores it. The set is a closed enum: Rust and TypeScript refuse a message whose code is not listed, so adding a code is one coordinated change across every runtime (this schema, the C# factory, the Rust CultNetErrorCode, and a captured-bytes case for each).",
|
|
"type": ["string", "null"],
|
|
"enum": [null, "selection_invalid", "cursor_stale", "cursor_invalid", "reference_outside_target", "variant_unsupported", "unowned_schema"]
|
|
},
|
|
"details": {
|
|
"description": "Code-specific structured detail (R-N). Every field is populated only for the code that defines it; the others are null, not omitted.",
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"required": ["field", "value", "asOf", "current"],
|
|
"properties": {
|
|
"field": { "description": "selection_invalid: the selection field that failed. variant_unsupported: \"subscriptionId\", when a subscription was ended. unowned_schema: \"schemaId\".", "type": ["string", "null"] },
|
|
"value": { "description": "selection_invalid: the offending value, when there is one string worth naming. variant_unsupported: the id of the subscription that ended. unowned_schema: the schema id no shard owns.", "type": ["string", "null"] },
|
|
"asOf": { "description": "cursor_stale: the cursor's own minted asOf.", "type": ["integer", "null"], "minimum": 0 },
|
|
"current": { "description": "cursor_stale: the answering server's current asOf.", "type": ["integer", "null"], "minimum": 0 }
|
|
}
|
|
}
|
|
}
|
|
}
|