CultLib/contracts/cultnet/interop/write-receipt-vectors.json
soul cc5a9cec9b tests first: repeated messageId key, each runtime's declared policy (C# failing)
Vectors admittedMessageIdDuplicates carry [invalid,valid], [valid,invalid], [valid,valid]. Decoders that see the
duplicate (C#, Rust) must refuse all three; map-collapsing decoders (Python, TS, Kotlin) judge the last key.
Python, TS, Kotlin and Rust already behave as declared; C# fails: it accepted [valid,valid].

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 16:04:58 +00:00

1850 lines
62 KiB
JSON

{
"provenance": "Spec-authored with msgpack-python 1.2.4 (str keys in schema order, use_bin_type). Not yet a C# capture: the first conformance PR for each runtime re-captures these with its own encoder and the bytes must match, as error-vectors.json does. Every schema case is checked against cultnet.write-admitted.schema.json / cultnet.error.v1.schema.json in this directory.",
"specification": "write-receipt.md",
"wire": [
{
"name": "write_admitted_put",
"messagePackHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A57075742D31A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "put-1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"name": "write_admitted_delete",
"messagePackHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A564656C2D31A96F7065726174696F6EA664656C657465A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "del-1",
"operation": "delete",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"name": "error_v1_write_not_authorized_echoes_id",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72D93677726974655F6E6F745F617574686F72697A65643A2074686520636174616C6F6720726566757365642074686520646F63756D656E74AB726F7574696E6748696E74C0A4636F6465B477726974655F6E6F745F617574686F72697A6564A764657461696C7384A56669656C64A97265636F72644B6579A576616C7565A66E6F74653A32A461734F66C0A763757272656E74C0A96D6573736167654964A57075742D32",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "write_not_authorized: the catalog refused the document",
"routingHint": null,
"code": "write_not_authorized",
"details": {
"field": "recordKey",
"value": "note:2",
"asOf": null,
"current": null
},
"messageId": "put-2"
}
},
{
"name": "error_v1_message_invalid_names_member_of_a_batch",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72D9256D6573736167655F696E76616C69643A207265636F72644B6579206973206D697373696E67AB726F7574696E6748696E74C0A4636F6465AF6D6573736167655F696E76616C6964A764657461696C7384A56669656C64A97265636F72644B6579A576616C7565C0A461734F66C0A763757272656E74C0A96D6573736167654964B062617463682D312D6D656D6265722D33",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid: recordKey is missing",
"routingHint": null,
"code": "message_invalid",
"details": {
"field": "recordKey",
"value": null,
"asOf": null,
"current": null
},
"messageId": "batch-1-member-3"
}
},
{
"name": "error_v1_unreadable_frame_has_null_message_id",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72D92E6D6573736167655F696E76616C69643A20746865206D65737361676520636F756C64206E6F742062652072656164AB726F7574696E6748696E74C0A4636F6465AF6D6573736167655F696E76616C6964A764657461696C7384A56669656C64A76D657373616765A576616C7565C0A461734F66C0A763757272656E74C0A96D6573736167654964C0",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid: the message could not be read",
"routingHint": null,
"code": "message_invalid",
"details": {
"field": "message",
"value": null,
"asOf": null,
"current": null
},
"messageId": null
}
},
{
"name": "error_v1_empty_schema_id_is_unreadable_and_names_the_write",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72D92E6D6573736167655F696E76616C69643A20746865206D65737361676520636F756C64206E6F742062652072656164AB726F7574696E6748696E74C0A4636F6465AF6D6573736167655F696E76616C6964A764657461696C7384A56669656C64A76D657373616765A576616C7565C0A461734F66C0A763757272656E74C0A96D6573736167654964A57075742D31",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid: the message could not be read",
"routingHint": null,
"code": "message_invalid",
"details": {
"field": "message",
"value": null,
"asOf": null,
"current": null
},
"messageId": "put-1"
}
},
{
"name": "error_v1_limit_reached",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72D93B6C696D69745F726561636865643A207468652073657373696F6E27732072656D656D62657265642D7772697465206C696D69742069732066756C6CAB726F7574696E6748696E74C0A4636F6465AD6C696D69745F72656163686564A764657461696C7384A56669656C64C0A576616C7565C0A461734F66C0A763757272656E74C0A96D6573736167654964A87075742D31303235",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "limit_reached: the session's remembered-write limit is full",
"routingHint": null,
"code": "limit_reached",
"details": {
"field": null,
"value": null,
"asOf": null,
"current": null
},
"messageId": "put-1025"
}
}
],
"blankFields": {
"semantics": "A put whose document 'names nothing' is refused like a frame that does not read (error.v1 message_invalid, details.field \"message\", the write's id, the fixed text of error_v1_empty_schema_id_is_unreadable_and_names_the_write) and never applied, on every connection, receipt-capable or not. Each case starts from this valid document: schemaId note.v0, recordKey note:1, storedAt 2026-11-08T00:00:00Z, payload 81A16101 (hex), no source ids, no tags; 'set' replaces or adds the named fields. A blank string is empty or made only of Unicode White_Space code points (the list in whiteSpaceCodePoints, the PropList property, the same in every runtime; U+200B, U+FEFF, U+180E and U+001C to U+001F are not White_Space, though some platforms' whitespace tests accept some of them). Fields not listed are valid. 'unset' means the key is absent or nil, which is not blank: those cases are admitted.",
"validPutPayloadHex": "81A16101",
"whiteSpaceCodePoints": ["0009","000A","000B","000C","000D","0020","0085","00A0","1680","2000","2001","2002","2003","2004","2005","2006","2007","2008","2009","200A","2028","2029","202F","205F","3000"],
"notWhiteSpaceCodePoints": ["200B","200C","200D","2060","FEFF","180E","001C","001D","001E","001F"],
"codePointCases": "For every code point in whiteSpaceCodePoints, each of schemaId, recordKey, storedAt, sourceRuntimeId, sourceAgentId, sourceRole and one tags entry, set alone to a string of just that code point (and to that code point repeated three times), is refused; for every code point in notWhiteSpaceCodePoints the same string is not blank and the put is admitted.",
"cases": [
{ "name": "valid_document_is_admitted", "set": {}, "outcome": "admitted" },
{ "name": "empty_schema_id", "set": { "schemaId": "" }, "outcome": "refused" },
{ "name": "blank_schema_id", "set": { "schemaId": " " }, "outcome": "refused" },
{ "name": "empty_record_key", "set": { "recordKey": "" }, "outcome": "refused" },
{ "name": "blank_record_key", "set": { "recordKey": " " }, "outcome": "refused" },
{ "name": "empty_stored_at", "set": { "storedAt": "" }, "outcome": "refused" },
{ "name": "blank_stored_at", "set": { "storedAt": " " }, "outcome": "refused" },
{ "name": "empty_payload", "set": { "payloadHex": "" }, "outcome": "refused" },
{ "name": "blank_source_runtime_id", "set": { "sourceRuntimeId": "" }, "outcome": "refused" },
{ "name": "blank_source_agent_id", "set": { "sourceAgentId": " " }, "outcome": "refused" },
{ "name": "blank_source_role", "set": { "sourceRole": "" }, "outcome": "refused" },
{ "name": "blank_tag", "set": { "tags": ["a", " "] }, "outcome": "refused" },
{ "name": "nbsp_schema_id", "set": { "schemaId": "\u00a0" }, "outcome": "refused" },
{ "name": "em_space_record_key", "set": { "recordKey": "\u2003\u2003" }, "outcome": "refused" },
{ "name": "next_line_stored_at", "set": { "storedAt": "\u0085" }, "outcome": "refused" },
{ "name": "line_separator_source_runtime_id", "set": { "sourceRuntimeId": "\u2028" }, "outcome": "refused" },
{ "name": "ideographic_space_source_agent_id", "set": { "sourceAgentId": "\u3000" }, "outcome": "refused" },
{ "name": "nbsp_source_role", "set": { "sourceRole": " \u00a0 " }, "outcome": "refused" },
{ "name": "em_space_tag", "set": { "tags": ["a", "\u2003"] }, "outcome": "refused" },
{ "name": "next_line_tag", "set": { "tags": ["\u0085"] }, "outcome": "refused" },
{ "name": "zero_width_space_schema_id_is_a_name", "set": { "schemaId": "\u200b" }, "outcome": "admitted" },
{ "name": "zero_width_space_record_key_is_a_name", "set": { "recordKey": "\u200b" }, "outcome": "admitted" },
{ "name": "zero_width_space_tag_is_a_name", "set": { "tags": ["\u200b"] }, "outcome": "admitted" },
{ "name": "bom_stored_at_is_a_name", "set": { "storedAt": "\ufeff" }, "outcome": "admitted" },
{ "name": "mongolian_vowel_separator_source_role_is_a_name", "set": { "sourceRole": "\u180e" }, "outcome": "admitted" },
{ "name": "padded_name_is_a_name", "set": { "schemaId": "\u00a0a\u2003", "tags": [" a"] }, "outcome": "admitted" },
{ "name": "file_separator_schema_id_is_a_name", "set": { "schemaId": "\u001c" }, "outcome": "admitted" },
{ "name": "source_ids_and_tags_present_and_named", "set": { "sourceRuntimeId": "r", "sourceAgentId": "a", "sourceRole": "w", "tags": ["a"] }, "outcome": "admitted" },
{ "name": "source_ids_and_tags_nil", "set": { "sourceRuntimeId": null, "sourceAgentId": null, "sourceRole": null, "tags": null }, "outcome": "admitted" },
{ "name": "empty_tag_list", "set": { "tags": [] }, "outcome": "admitted" }
]
},
"admittedMessageId": {
"semantics": "Decoder-side rule for a `cultnet.write_admitted.v0` frame's `messageId` (Heimdall's ruling, write-receipt.md). Valid only if it contains at least one Unicode letter (\\p{L}) or number (\\p{N}) AND no Default_Ignorable_Code_Point (DerivedCoreProperties). The compare is RAW: the id is echoed byte for byte, so a decoder neither trims nor normalizes. Each case's id is given as `codePoints` (hex, in order), repeated `repeat` times (default 1), so no lone surrogate appears in this file. Every messageId key of a frame must be valid. A runner builds the frame {schemaVersion write_admitted.v0, messageId, operation put, schemaId note.v0, recordKey note:1} with its own encoder and decodes it with the write_admitted decoder; `refuse` means the decoder refuses the frame the way it refuses an empty or ill-typed messageId, `accept` means it decodes and the id comes back identical. A case with `wireHex` carries the whole frame in MessagePack hex, for an id a language string or this file cannot hold (a lone surrogate is not valid UTF-8; the bytes are ED A0 80) or a frame that repeats a key. The rule is on the decoded string, which must be well-formed Unicode: a lone surrogate refuses. A runtime whose MessagePack reader rejects invalid UTF-8 refuses earlier; byte-level strictness is a property of the reader and is not required of a decoder. Scope: write_admitted only; error.v1 is untouched.",
"cases": [
{
"name": "refuse_braille_blank_2800",
"codePoints": [
"2800"
],
"expect": "refuse",
"why": "U+2800 is Default_Ignorable and not a letter or number"
},
{
"name": "refuse_hangul_filler_3164",
"codePoints": [
"3164"
],
"expect": "refuse",
"why": "U+3164 Default_Ignorable (a letter by category Lo, so DI decides)"
},
{
"name": "refuse_hangul_choseong_filler_115F",
"codePoints": [
"115F"
],
"expect": "refuse",
"why": "U+115F Default_Ignorable (category Lo)"
},
{
"name": "refuse_halfwidth_hangul_filler_FFA0",
"codePoints": [
"FFA0"
],
"expect": "refuse",
"why": "U+FFA0 Default_Ignorable (category Lo)"
},
{
"name": "refuse_combining_grapheme_joiner_034F",
"codePoints": [
"034F"
],
"expect": "refuse",
"why": "U+034F Default_Ignorable"
},
{
"name": "refuse_combining_mark_alone_0300",
"codePoints": [
"0300"
],
"expect": "refuse",
"why": "a combining mark is not a letter or number"
},
{
"name": "refuse_private_use_E000",
"codePoints": [
"E000"
],
"expect": "refuse",
"why": "private use is neither a letter nor a number"
},
{
"name": "refuse_unassigned_0378",
"codePoints": [
"0378"
],
"expect": "refuse",
"why": "unassigned since Unicode 1.1, neither a letter nor a number"
},
{
"name": "refuse_lone_surrogate_D800",
"codePoints": [
"D800"
],
"expect": "refuse",
"why": "a lone surrogate is not a Unicode scalar value and has no letter or number; on the wire it is the invalid UTF-8 bytes ED A0 80 in a str. A reader that rejects invalid UTF-8 refuses the frame; one that substitutes or keeps the surrogate refuses the id",
"wireHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A3EDA080A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31"
},
{
"name": "refuse_control_nul_0000",
"codePoints": [
"0000"
],
"expect": "refuse",
"why": "a control character is neither a letter nor a number"
},
{
"name": "refuse_control_bel_0007",
"codePoints": [
"0007"
],
"expect": "refuse",
"why": "a control character is neither a letter nor a number"
},
{
"name": "refuse_control_unit_separator_001F",
"codePoints": [
"001F"
],
"expect": "refuse",
"why": "a control character is neither a letter nor a number"
},
{
"name": "refuse_control_delete_007F",
"codePoints": [
"007F"
],
"expect": "refuse",
"why": "a control character is neither a letter nor a number"
},
{
"name": "refuse_control_next_line_0085",
"codePoints": [
"0085"
],
"expect": "refuse",
"why": "a control character is neither a letter nor a number"
},
{
"name": "refuse_empty",
"codePoints": [],
"expect": "refuse",
"why": "empty"
},
{
"name": "refuse_whitespace_only_space_0020",
"codePoints": [
"0020"
],
"expect": "refuse",
"why": "white space only"
},
{
"name": "refuse_whitespace_only_nbsp_00A0",
"codePoints": [
"00A0"
],
"expect": "refuse",
"why": "white space only"
},
{
"name": "refuse_whitespace_only_ideographic_space_3000",
"codePoints": [
"3000"
],
"expect": "refuse",
"why": "white space only"
},
{
"name": "refuse_letter_zwsp_letter",
"codePoints": [
"0061",
"200B",
"0062"
],
"expect": "refuse",
"why": "a Default_Ignorable (U+200B) beside letters"
},
{
"name": "refuse_letter_cgj",
"codePoints": [
"0061",
"034F"
],
"expect": "refuse",
"why": "a Default_Ignorable (U+034F) after a letter"
},
{
"name": "refuse_letter_soft_hyphen",
"codePoints": [
"0061",
"00AD"
],
"expect": "refuse",
"why": "U+00AD is Default_Ignorable"
},
{
"name": "refuse_letter_zwj",
"codePoints": [
"0061",
"200D"
],
"expect": "refuse",
"why": "U+200D is Default_Ignorable"
},
{
"name": "refuse_digit_mongolian_vowel_separator",
"codePoints": [
"0031",
"180E"
],
"expect": "refuse",
"why": "U+180E is Default_Ignorable since Unicode 6.3"
},
{
"name": "refuse_digit_word_joiner_digit",
"codePoints": [
"0031",
"2060",
"0032"
],
"expect": "refuse",
"why": "U+2060 is Default_Ignorable"
},
{
"name": "refuse_bom_before_letter",
"codePoints": [
"FEFF",
"0061"
],
"expect": "refuse",
"why": "U+FEFF is Default_Ignorable; JavaScript trim() would strip it and accept the rest, but the compare is raw"
},
{
"name": "accept_unicode16_gurung_khema_letter_16100",
"codePoints": [
"16100"
],
"expect": "accept",
"why": "U+16100 GURUNG KHEMA LETTER A is Lo and new in Unicode 16.0 (unassigned in 15.1); the rule is pinned to 16.0 in every runtime"
},
{
"name": "accept_unicode16_garay_digit_zero_10D40",
"codePoints": [
"10D40"
],
"expect": "accept",
"why": "U+10D40 GARAY DIGIT ZERO is Nd and new in Unicode 16.0"
},
{
"name": "accept_unicode15_1_cjk_ext_i_2EBF0",
"codePoints": [
"2EBF0"
],
"expect": "accept",
"why": "U+2EBF0, first of CJK Unified Ideographs Extension I, is Lo and new in Unicode 15.1"
},
{
"name": "accept_last_cjk_ext_i_2EE5D",
"codePoints": [
"2EE5D"
],
"expect": "accept",
"why": "U+2EE5D is the last assigned code point of Extension I (Lo)"
},
{
"name": "refuse_after_cjk_ext_i_2EE5E",
"codePoints": [
"2EE5E"
],
"expect": "refuse",
"why": "U+2EE5E is unassigned in Unicode 16.0, so neither a letter nor a number"
},
{
"name": "refuse_unassigned_in_16_0_10D90",
"codePoints": [
"10D90"
],
"expect": "refuse",
"why": "U+10D90 follows the Garay block and is unassigned in Unicode 16.0"
},
{
"name": "refuse_duplicate_message_id_second_is_default_ignorable",
"codePoints": ["0061"],
"expect": "refuse",
"why": "the frame carries messageId twice, \"a\" and then U+200B; every messageId key must be valid, and a reader that keeps the last one sees U+200B",
"wireHex": "86AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A57075742D31A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31A96D6573736167654964A3E2808B"
},
{
"name": "refuse_letter_then_lone_surrogate_D800",
"codePoints": ["0061"],
"expect": "refuse",
"why": "\"a\" followed by a lone surrogate: the id as decoded is not well-formed Unicode. A reader that rejects invalid UTF-8 refuses the frame earlier; one that substitutes or keeps the surrogate must still refuse the id. Byte-level strictness is a property of the reader, not required of the decoder",
"wireHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A461EDA080A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31"
},
{
"name": "accept_astral_mathematical_bold_capital_a_1D400",
"codePoints": ["1D400"],
"expect": "accept",
"why": "an astral letter (Lu)"
},
{
"name": "accept_astral_cjk_ext_b_20000",
"codePoints": ["20000"],
"expect": "accept",
"why": "an astral letter (Lo)"
},
{
"name": "refuse_letter_then_default_ignorable_061C",
"codePoints": ["0061", "061C"],
"expect": "refuse",
"why": "U+061C is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_17B4",
"codePoints": ["0061", "17B4"],
"expect": "refuse",
"why": "U+17B4 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_17B5",
"codePoints": ["0061", "17B5"],
"expect": "refuse",
"why": "U+17B5 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_202A",
"codePoints": ["0061", "202A"],
"expect": "refuse",
"why": "U+202A is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_202E",
"codePoints": ["0061", "202E"],
"expect": "refuse",
"why": "U+202E is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_FE00",
"codePoints": ["0061", "FE00"],
"expect": "refuse",
"why": "U+FE00 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_FE0F",
"codePoints": ["0061", "FE0F"],
"expect": "refuse",
"why": "U+FE0F is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_FFF0",
"codePoints": ["0061", "FFF0"],
"expect": "refuse",
"why": "U+FFF0 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_FFF8",
"codePoints": ["0061", "FFF8"],
"expect": "refuse",
"why": "U+FFF8 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_1BCA0",
"codePoints": ["0061", "1BCA0"],
"expect": "refuse",
"why": "U+1BCA0 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_1BCA3",
"codePoints": ["0061", "1BCA3"],
"expect": "refuse",
"why": "U+1BCA3 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_1D173",
"codePoints": ["0061", "1D173"],
"expect": "refuse",
"why": "U+1D173 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_1D17A",
"codePoints": ["0061", "1D17A"],
"expect": "refuse",
"why": "U+1D17A is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_E0000",
"codePoints": ["0061", "E0000"],
"expect": "refuse",
"why": "U+E0000 is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "refuse_letter_then_default_ignorable_E0FFF",
"codePoints": ["0061", "E0FFF"],
"expect": "refuse",
"why": "U+E0FFF is Default_Ignorable (an end of one of its ranges)"
},
{
"name": "accept_abc",
"codePoints": [
"0061",
"0062",
"0063"
],
"expect": "accept",
"why": "letters"
},
{
"name": "accept_digit_1",
"codePoints": [
"0031"
],
"expect": "accept",
"why": "a number"
},
{
"name": "accept_padded_a_raw_compare",
"codePoints": [
"0020",
"0061",
"0020"
],
"expect": "accept",
"why": "raw compare: the id is echoed byte for byte, never trimmed, so the padding stays and the letter makes it valid"
},
{
"name": "accept_e_acute_precomposed",
"codePoints": [
"00E9"
],
"expect": "accept",
"why": "a letter"
},
{
"name": "accept_arabic_indic_digit_three",
"codePoints": [
"0663"
],
"expect": "accept",
"why": "category Nd, a number"
},
{
"name": "accept_cjk_zhong",
"codePoints": [
"4E2D"
],
"expect": "accept",
"why": "a letter (Lo, not Default_Ignorable)"
},
{
"name": "accept_letter_then_combining_acute",
"codePoints": [
"0065",
"0301"
],
"expect": "accept",
"why": "a combining mark after a letter is allowed"
},
{
"name": "accept_letter_nul_letter",
"codePoints": [
"0061",
"0000",
"0062"
],
"expect": "accept",
"why": "U+0000 is not Default_Ignorable and a letter is present, so the rule accepts; raw compare keeps the NUL"
},
{
"name": "accept_256_ascii_letters",
"codePoints": [
"0061"
],
"expect": "accept",
"why": "256 bytes is the size limit and is allowed",
"repeat": 256
}
]
},
"admittedMessageIdDuplicates": {
"semantics": "A frame with more than one messageId key is malformed. A decoder that can see the duplicate (C#, Rust: raw MessagePack reader or ordered pairs) refuses it, whatever the values (`seesDuplicate`). A decoder that collapses the map (Python, TypeScript, Kotlin) judges the value it ends up with, the last key, and refuses exactly when that value is invalid (`lastKeyWins`); where it accepts it echoes the last key's id (`lastKeyWinsEcho`). Neither behaviour ever echoes an invalid id. Each runtime declares which policy it has and its runner asserts that policy's column, so a change of a decoder's key policy fails a named test. The first key is ZWSP (U+200B, Default_Ignorable) in `invalid_then_valid`; the second key is the last entry of the map. Frames are MessagePack hex.",
"cases": [
{
"name": "invalid_then_valid",
"wireHex": "86AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A3E2808BA96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31A96D6573736167654964A161",
"seesDuplicate": "refuse",
"lastKeyWins": "accept",
"lastKeyWinsEcho": "a"
},
{
"name": "valid_then_invalid",
"wireHex": "86AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A161A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31A96D6573736167654964A3E2808B",
"seesDuplicate": "refuse",
"lastKeyWins": "refuse"
},
{
"name": "valid_then_valid",
"wireHex": "86AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A161A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31A96D6573736167654964A162",
"seesDuplicate": "refuse",
"lastKeyWins": "accept",
"lastKeyWinsEcho": "b"
}
]
},
"schemaInvalid": [
{
"name": "write_admitted_with_an_error_key",
"messagePackHex": "86AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A57075742D31A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31A56572726F72A46E6F7065",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "put-1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1",
"error": "nope"
},
"why": "admission must not carry an error"
},
{
"name": "write_admitted_empty_message_id",
"messagePackHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A0A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
},
"why": "messageId is non-empty"
},
{
"name": "write_admitted_unknown_operation",
"messagePackHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A57075742D31A96F7065726174696F6EA6757073657274A8736368656D614964A76E6F74652E7630A97265636F72644B6579A66E6F74653A31",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "put-1",
"operation": "upsert",
"schemaId": "note.v0",
"recordKey": "note:1"
},
"why": "operation is put or delete"
},
{
"name": "write_admitted_null_record_key",
"messagePackHex": "85AD736368656D6156657273696F6EB963756C746E65742E77726974655F61646D69747465642E7630A96D6573736167654964A57075742D31A96F7065726174696F6EA3707574A8736368656D614964A76E6F74652E7630A97265636F72644B6579C0",
"message": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "put-1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": null
},
"why": "map-mode keys are never nil"
},
{
"name": "error_v1_without_message_id",
"messagePackHex": "85AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72AF6D6573736167655F696E76616C6964AB726F7574696E6748696E74C0A4636F6465AF6D6573736167655F696E76616C6964A764657461696C7384A56669656C64A76D657373616765A576616C7565C0A461734F66C0A763757272656E74C0",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid",
"routingHint": null,
"code": "message_invalid",
"details": {
"field": "message",
"value": null,
"asOf": null,
"current": null
}
},
"why": "messageId is required (null when unknown), never omitted"
},
{
"name": "error_v1_empty_message_id",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7631A56572726F72AF6D6573736167655F696E76616C6964AB726F7574696E6748696E74C0A4636F6465AF6D6573736167655F696E76616C6964A764657461696C7384A56669656C64A76D657373616765A576616C7565C0A461734F66C0A763757272656E74C0A96D6573736167654964A0",
"message": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid",
"routingHint": null,
"code": "message_invalid",
"details": {
"field": "message",
"value": null,
"asOf": null,
"current": null
},
"messageId": ""
},
"why": "null, not empty string, means unknown"
},
{
"name": "error_v0_is_not_error_v1",
"messagePackHex": "86AD736368656D6156657273696F6EB063756C746E65742E6572726F722E7630A56572726F72B477726974655F6E6F745F617574686F72697A6564AB726F7574696E6748696E74C0A4636F6465B477726974655F6E6F745F617574686F72697A6564A764657461696C7384A56669656C64A97265636F72644B6579A576616C7565A16BA461734F66C0A763757272656E74C0A96D6573736167654964A57075742D32",
"message": {
"schemaVersion": "cultnet.error.v0",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": {
"field": "recordKey",
"value": "k",
"asOf": null,
"current": null
},
"messageId": "put-2"
},
"why": "v0 stays closed: a messageId key makes it invalid, which is why v1 exists"
}
],
"scenarios": {
"semantics": "Each scenario replays events in atMs order against a client that sent each write at its send event; a tick advances the clock without a frame. An event is atMs plus exactly one of: send (a write goes out), frame (a decoded frame arrives), transportAck (delivery, never an answer; the tracker has no input for it), tick (the clock advances), sendFailed (the client's own send of that write failed), sessionEnd (the session ended). Any other key is a defect in the vectors or the runner. A window ends when a write's age reaches it, so an event at exactly sentAt+window finds the write already unconfirmed. 'window' names the constant in vectors.constants that applies (server receipt -> RECEIPT_RESULT_WINDOW_MS, legacy -> LEGACY_RESULT_WINDOW_MS); a tick at or after the window settles unanswered writes as unconfirmed. expect gives the outcome the caller was told per write id; recorded, when present, is the write handle's later state. admitted/refused/unconfirmed are three distinct results; no scenario may settle admitted without a write_admitted frame carrying the write's id.",
"cases": [
{
"name": "receipt_admits",
"description": "A receipt with the id completes the write as admitted.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"transportAck": "w1"
},
{
"atMs": 20,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "receipt_before_transport_ack",
"description": "Order against the transport ack is irrelevant.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 9,
"transportAck": "w1"
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "refusal_after_ack",
"description": "A typed refusal after the transport ack refuses; the ack was not admission.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"transportAck": "w1"
},
{
"atMs": 120,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null,
"messageId": "w1"
}
}
],
"expect": {
"w1": {
"outcome": "refused",
"code": "write_not_authorized"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "refusal_before_ack",
"description": "A typed refusal before the transport ack refuses.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid",
"routingHint": null,
"code": "message_invalid",
"details": null,
"messageId": "w1"
}
},
{
"atMs": 9,
"transportAck": "w1"
}
],
"expect": {
"w1": {
"outcome": "refused",
"code": "message_invalid"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "ack_only_window_ends_unconfirmed",
"description": "The legacy fallback: an acked write that nothing answered ends unconfirmed, never admitted.",
"server": "legacy",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"transportAck": "w1"
},
{
"atMs": 500,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "LEGACY_RESULT_WINDOW_MS"
},
{
"name": "window_runs_from_send_not_from_ack",
"description": "A late transport ack does not extend the window.",
"server": "legacy",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 450,
"transportAck": "w1"
},
{
"atMs": 500,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "LEGACY_RESULT_WINDOW_MS"
},
{
"name": "silent_receipt_capable_server_is_unconfirmed_too",
"description": "A server that advertised receipts and stayed silent is still only unconfirmed, and the client reports the fault.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"transportAck": "w1"
},
{
"atMs": 5000,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "legacy_error_with_one_write_in_flight_refuses",
"description": "A pre-receipt server answers with error.v0 and no id: attributable only because one write is unanswered.",
"server": "legacy",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 30,
"frame": {
"schemaVersion": "cultnet.error.v0",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null
}
}
],
"expect": {
"w1": {
"outcome": "refused",
"code": "write_not_authorized"
}
},
"window": "LEGACY_RESULT_WINDOW_MS"
},
{
"name": "legacy_error_with_two_writes_in_flight_is_unattributed",
"description": "An id-less error with two unanswered writes names neither: both stay pending and end unconfirmed.",
"server": "legacy",
"writes": [
"w1",
"w2"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 1,
"send": "w2"
},
{
"atMs": 30,
"frame": {
"schemaVersion": "cultnet.error.v0",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null
}
},
{
"atMs": 501,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
},
"w2": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "LEGACY_RESULT_WINDOW_MS"
},
{
"name": "batch_names_the_refused_member",
"description": "A batch is independent writes. The refusal names its member; the others keep their own outcomes.",
"server": "receipt",
"writes": [
"b1",
"b2",
"b3"
],
"events": [
{
"atMs": 0,
"send": "b1"
},
{
"atMs": 0,
"send": "b2"
},
{
"atMs": 0,
"send": "b3"
},
{
"atMs": 10,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "b1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 11,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null,
"messageId": "b2"
}
},
{
"atMs": 12,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "b3",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"b1": {
"outcome": "admitted"
},
"b2": {
"outcome": "refused",
"code": "write_not_authorized"
},
"b3": {
"outcome": "admitted"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "batch_with_one_member_unanswered",
"description": "A member nothing answered is unconfirmed; the batch as a whole is admitted only if every member is.",
"server": "receipt",
"writes": [
"b1",
"b2"
],
"events": [
{
"atMs": 0,
"send": "b1"
},
{
"atMs": 0,
"send": "b2"
},
{
"atMs": 10,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "b1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 5000,
"tick": true
}
],
"expect": {
"b1": {
"outcome": "admitted"
},
"b2": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "session_ends_before_an_answer",
"description": "A lost session leaves unanswered writes unconfirmed, not failed and not admitted.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"transportAck": "w1"
},
{
"atMs": 40,
"sessionEnd": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "session_ended"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "late_refusal_is_recorded_not_told",
"description": "An answer after the window does not change what the caller was told; the handle records refused_late.",
"server": "legacy",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 500,
"tick": true
},
{
"atMs": 700,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null,
"messageId": "w1"
}
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed",
"recorded": "refused_late"
}
},
"window": "LEGACY_RESULT_WINDOW_MS"
},
{
"name": "duplicate_resend_of_an_admitted_write_is_admitted_again",
"description": "The server answers a re-sent admitted messageId with the receipt again, never an error.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 20,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 30,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "an_answer_for_an_unknown_id_is_ignored",
"description": "A frame naming no write in flight settles nothing.",
"server": "receipt",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 10,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "other",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 5000,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
},
"window": "RECEIPT_RESULT_WINDOW_MS"
},
{
"name": "late_receipt_is_recorded_admitted_late",
"description": "A receipt after the window: the caller was told unconfirmed, the handle records admitted_late; a caller must consult it before resending.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5000,
"tick": true
},
{
"atMs": 6200,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed",
"recorded": "admitted_late"
}
}
},
{
"name": "receipt_inside_the_receipt_window_is_not_late",
"description": "The receipt-capable window is 5 s: a receipt at 1.2 s admits.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 1200,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
}
},
{
"name": "legacy_window_is_not_used_against_a_receipt_server",
"description": "At 500 ms a receipt-capable server's write is still pending, not unconfirmed.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 500,
"tick": true
},
{
"atMs": 800,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
}
},
{
"name": "error_v1_without_id_with_one_write_in_flight_refuses",
"description": "An error.v1 whose messageId is nil names no write: like error.v0 it is attributable only because one write is unanswered.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 30,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid",
"routingHint": null,
"code": "message_invalid",
"details": null,
"messageId": null
}
}
],
"expect": {
"w1": {
"outcome": "refused",
"code": "message_invalid"
}
}
},
{
"name": "error_v1_without_id_with_two_writes_in_flight_is_unattributed",
"description": "An error.v1 with a nil messageId settles neither of two unanswered writes; each ends unconfirmed at its window.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1",
"w2"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 1,
"send": "w2"
},
{
"atMs": 30,
"frame": {
"schemaVersion": "cultnet.error.v1",
"error": "message_invalid",
"routingHint": null,
"code": "message_invalid",
"details": null,
"messageId": null
}
},
{
"atMs": 5001,
"tick": true
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
},
"w2": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
}
},
{
"name": "send_failed_is_unconfirmed_and_touches_no_other_write",
"description": "The client's own send of w1 failed: w1 is unconfirmed local_send_failed, never refused; w2 is unaffected and settles on its own receipt.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1",
"w2"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 1,
"send": "w2"
},
{
"atMs": 2,
"sendFailed": "w1"
},
{
"atMs": 20,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w2",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "local_send_failed"
},
"w2": {
"outcome": "admitted"
}
}
},
{
"name": "error_without_id_after_the_window_settles_nothing",
"description": "Once w1 is unconfirmed no write is unanswered, so an error that names no write is attributed to nothing and records nothing on w1.",
"server": "legacy",
"window": "LEGACY_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 500,
"tick": true
},
{
"atMs": 600,
"frame": {
"schemaVersion": "cultnet.error.v0",
"error": "write_not_authorized",
"routingHint": null,
"code": "write_not_authorized",
"details": null
}
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed"
}
}
},
{
"name": "receipt_one_millisecond_inside_the_window_admits",
"description": "A receipt 1 ms before the window ends settles the write admitted.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 4999,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "admitted"
}
}
},
{
"name": "receipt_at_the_exact_window_boundary_is_late",
"description": "The window ends when the write's age reaches it: a receipt at exactly that instant finds w1 already unconfirmed and is recorded admitted_late.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5000,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
}
],
"expect": {
"w1": {
"outcome": "unconfirmed",
"reason": "window_elapsed",
"recorded": "admitted_late"
}
}
},
{
"name": "send_after_session_end_is_unconfirmed_at_once",
"description": "A write registered after the session ended can never be answered: it settles unconfirmed session_ended when sent, and the earlier write keeps its own receipt.",
"server": "receipt",
"window": "RECEIPT_RESULT_WINDOW_MS",
"writes": [
"w1",
"w2"
],
"events": [
{
"atMs": 0,
"send": "w1"
},
{
"atMs": 5,
"frame": {
"schemaVersion": "cultnet.write_admitted.v0",
"messageId": "w1",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1"
}
},
{
"atMs": 40,
"sessionEnd": true
},
{
"atMs": 50,
"send": "w2"
}
],
"expect": {
"w1": {
"outcome": "admitted"
},
"w2": {
"outcome": "unconfirmed",
"reason": "session_ended"
}
}
}
]
},
"constants": {
"RECEIPT_RESULT_WINDOW_MS": 5000,
"LEGACY_RESULT_WINDOW_MS": 500,
"DEDUPE_RETENTION_MS": 600000
},
"counts": {
"semantics": "How many of each a runner must have run. A runner that reads fewer (a parser that drops entries, a loop over nothing) fails instead of passing quietly; a runner that meets an event key outside scenarios.semantics fails too.",
"wire": 7,
"schemaInvalid": 7,
"contentHashCases": 4,
"scenarioCases": 25,
"admittedMessageIdCases": 59,
"admittedMessageIdDuplicateCases": 3
},
"contentHash": {
"definition": "sha256(msgpack([operation, schemaId, recordKey, payload])), payload a msgpack bin for a put and nil for a delete; hex lowercase",
"cases": [
{
"name": "put_note_1_payload_a",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1",
"payloadHex": "81A26964A131",
"sha256": "7051c10071123b6ade50fbd5075e3859cae4ea70ce358a24a0b0c6980090e95d"
},
{
"name": "put_note_1_payload_b",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:1",
"payloadHex": "81A26964A132",
"sha256": "58d3fc6b858d2605a68550c9d4811ccdca6b9db7a0c3bfebf304d279257f6f9f"
},
{
"name": "put_note_2_payload_a",
"operation": "put",
"schemaId": "note.v0",
"recordKey": "note:2",
"payloadHex": "81A26964A131",
"sha256": "a248cd920e58d41bc6ccca9e108030b1d491239000047fef088122364a32b6f8"
},
{
"name": "delete_note_1",
"operation": "delete",
"schemaId": "note.v0",
"recordKey": "note:1",
"payloadHex": null,
"sha256": "232b0f96651b28aa8ab7673a6106944a9f6a8d66852b6e21291a23ed084b8b37"
}
]
},
"dedupe": {
"semantics": "Replay writes in order against a receipt-capable server whose actors are transport-authenticated. expect lists the answer per write in order and how many times a write was applied to the store in total. 'invalid' means error.v1 message_invalid with details.field messageId and the write's id.",
"cases": [
{
"name": "resend_same_content_re_admits_without_reapplying",
"writes": [
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
}
],
"answers": [
"admitted",
"admitted"
],
"applied": 1
},
{
"name": "same_id_different_payload_is_refused_not_re_receipted",
"writes": [
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_b"
}
],
"answers": [
"admitted",
"refused:message_invalid"
],
"applied": 1
},
{
"name": "same_id_different_record_key_is_refused",
"writes": [
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_2_payload_a"
}
],
"answers": [
"admitted",
"refused:message_invalid"
],
"applied": 1
},
{
"name": "same_id_different_operation_is_refused",
"writes": [
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "alice",
"messageId": "m1",
"content": "delete_note_1"
}
],
"answers": [
"admitted",
"refused:message_invalid"
],
"applied": 1
},
{
"name": "the_same_id_from_another_actor_is_an_independent_write",
"writes": [
{
"actor": "alice",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "bob",
"messageId": "m1",
"content": "put_note_1_payload_b"
}
],
"answers": [
"admitted",
"admitted"
],
"applied": 2
},
{
"name": "resend_of_a_refused_write_is_refused_the_same_way",
"writes": [
{
"actor": "mallory",
"messageId": "m1",
"content": "put_note_1_payload_a"
},
{
"actor": "mallory",
"messageId": "m1",
"content": "put_note_1_payload_a"
}
],
"answers": [
"refused:write_not_authorized",
"refused:write_not_authorized"
],
"applied": 0,
"note": "mallory is not authorized to write note.v0"
},
{
"name": "the_capability_does_not_authorize",
"writes": [
{
"actor": "mallory",
"messageId": "m2",
"content": "put_note_2_payload_a"
}
],
"helloDeclaresReceipts": true,
"answers": [
"refused:write_not_authorized"
],
"applied": 0,
"note": "declaring the receipt capability changes the frame the refusal arrives in, never whether it is a refusal"
}
]
}
}