Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,36 @@ All notable changes to the CacheKit Protocol Specification.

### Test Vectors

- **`verify` now enforces the python-frame default-path twin claim** (LAB-3967,
follow-up to LAB-1203). The `bin` twin in
`test-vectors/python-frame.json` carries a new operator-owned
`"twin_of": "default_saas_write_msgpack_bytestorage"` field, and
`python-frame-reference.py verify` hard-fails any declared twin that differs
from its base beyond envelope encoding — `value_json`, frame-prefix bytes
(compared as bytes, so a header key reorder is caught), `compressed_data_hex`,
`checksum_hex`, `original_size`, `format`, `inner_msgpack_hex` — or names a
vector that does not exist. Until now that check ran only at `generate` time,
as a stderr warning nobody in CI reads. The gate is keyed on the declaration,
not on the bytes, because from the bytes alone "the wheel drifted" and "the
protocol legitimately moved while the legacy vector stayed frozen" are
indistinguishable — mirroring the byte compare into CI would have moved the
LAB-1203 generator deadlock one level up. `generate` keeps warning (never
raising) and now names the two exits: fix the wheel/codec, or drop `twin_of`
in the same commit as the regenerated bytes. `_upsert` carries `twin_of`
across rebuilds and never adds or drops it; the generator's `_bin`
description no longer makes the twin claim in prose (the field is the claim),
so the exit survives the next `generate`. A declaration that cannot hold —
pointing at itself, at a same-encoding copy, at an envelope-less or
partial-envelope vector, or into a fixture with duplicate vector names —
fails rather than passing vacuously (a missing envelope field is a `FAIL`
line, never a traceback). Pinned by `tools/test_python_frame_reference.py` (mutation suite
over the committed fixture). **Fixture sha256 changes** — JSON metadata only
(`twin_of` added, `_bin` description reworded); every `frame_hex`,
`expected_payload_hex`, `expected_header` and `payload_envelope` byte is
unchanged: `d8a3756a6814971f5a2c3e6573908f60789056ecf4a2f59c28bad525745a4b6c` →
`f43eb733ccccdd9b75e95f9695719733ec78c7e31f06d8db42f0cd12b5160ac7` (no SDK
vendored this fixture when this landed, so nothing downstream re-pins).

- `tools/python-frame-reference.py generate` now **upserts by vector name**
(LAB-1203): it rebuilds only the vectors the installed `cachekit` wheel can
reproduce and leaves every other committed vector byte-untouched, so dropping
Expand Down
10 changes: 10 additions & 0 deletions spec/wire-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,16 @@ round-trip) in both envelope encodings — the legacy array-of-ints original
release emitting `bin`) — an Arrow-envelope frame (structural checks), and
must-reject error vectors — including a CK frame fed to a strict interop reader.

The `bin` twin carries `"twin_of": "default_saas_write_msgpack_bytestorage"`: an
operator-owned declaration that it differs from the legacy vector **only** in
envelope encoding (same value, frame-prefix bytes, compressed bytes, checksum,
size, format and inner MessagePack). `verify` enforces the declaration as a hard
failure; `generate` never adds or removes it and only warns on divergence. When
the default write path legitimately moves, the exit is to drop `twin_of` from
the regenerated vector in the same commit — a reviewable fixture diff — not to
loosen a byte comparison. The legacy vector stays frozen (no installable wheel
emits the array-of-integers envelope any more) as legacy-read proof.

Verify:

```bash
Expand Down
3 changes: 2 additions & 1 deletion test-vectors/python-frame.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
},
{
"name": "default_saas_write_msgpack_bytestorage_bin",
"description": "Protocol 1.1 twin of default_saas_write_msgpack_bytestorage: same value, same default @cache write path, but the ByteStorage envelope's compressed_data is msgpack bin (serde_bytes) instead of an array of integers. Readers MUST accept both encodings; the legacy encoding stays pinned by the legacy vector's bytes.",
"description": "Default @cache write (StandardSerializer, integrity on) from a protocol 1.1 wheel: the ByteStorage envelope's compressed_data is msgpack bin (serde_bytes) instead of the legacy array of integers. Readers MUST accept both encodings; the legacy encoding stays pinned by default_saas_write_msgpack_bytestorage. When this vector carries 'twin_of', that field \u2014 not this text \u2014 is the claim that the two differ only in envelope encoding, and verify enforces it.",
"value_json": {
"user_id": 42,
"name": "cachekit",
Expand All @@ -97,6 +97,7 @@
"format": "msgpack",
"inner_msgpack_hex": "83a7757365725f69642aa46e616d65a863616368656b6974a6616374697665c3"
},
"twin_of": "default_saas_write_msgpack_bytestorage",
"generator": "cachekit 0.17.0 (PyPI wheel; Rust core via PyO3), generated by tools/python-frame-reference.py generate-bin-twin"
}
],
Expand Down
Loading
Loading