Repository navigation
Name frame versions once, and read a frame from what the file says - #94
Merged
Merged
Conversation
The plan's X-FRAME row (docs/release/1.0-plan.md:142) asks for a frame
registry, the v7 envelope, the read_frame*/describe_frame family and the
FrameFault classes, absorbing FRE-4, TAG-2 and ROB-8's fault_class naming.
weft_physical_type::frame_registry is now the only place that says which
frame version means what: 5 single-block, 6 paged, 7 envelope, and the rule
in the module doc that a number is never reused or renumbered once it has
shipped. The envelope is a header and a trailer around an existing frame,
WEFTSEG | u16 7 | u16 body_version | extension table | frame | crc32, and it
stores that frame verbatim, its own magic, version, body and CRC intact. That
is a deliberate constraint: segment_store.rs promises an unconfigured
deployment's bytes are byte-for-byte what previous versions wrote, and the
byte-pinning tests stay green because a v5 or v6 body is untouched. A legacy
reader pointed at an envelope still says UnsupportedVersion { found: 7 }
rather than misparsing it.
An extension carries a critical flag. Unknown and critical means refusal:
the body was written on the assumption that the extension would be applied,
so decoding without it serves data under a contract nobody honoured. Unknown
and optional means ignored. KNOWN_EXTENSIONS is empty on purpose - the
envelope ships with no extensions, and the numbers are a permanent namespace.
SegmentStore chose its decoder from the index row's format_version, so
anything that was not 6 - including the unwrap_or(0) in segment_index.rs -
sent a paged frame to the single-block reader. It now asks the file: the six
decode sites go through weftdb/src/types/frame_read.rs, which probes the
header, calls the matching read_frame_*, and returns failures as
weftdb::FrameFaultError in the anyhow chain under the unchanged
"decoding [paged ]segment <path>: ..." sentence. weft-server maps the class
by type: a frame ahead of this build is 503 incompatible_store, any other
frame fault is 500 frame_undecodable, the code X-ERR gave a wire sentence but
no producer.
No writer opts into 7 yet: seals still write 5 and 6. This slice is the
mechanism and the classes; the opt-in and the C5 quarantine behaviour belong
to D-S12 and the ingest lanes.
Tests: weft-physical-type 306 lib (14 new: envelope round-trip, a legacy
reader refusing an envelope, critical refusal, optional ignore, header/count
disagreement caught by CRC, describe_frame on a future version, fault
classification, the read family over a wrapped frame); weftdb frame_read 6
new; weft-server --test frame_fault 2 new, corrupting a real sealed frame
and asserting 500 frame_undecodable with details.fault "integrity" and 503
incompatible_store with "newer_format" after re-stamping the trailer CRC.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #93 (PAC-3) — base that branch for a reviewable diff, or merge downwards.
What this is
The 1.0 plan's X-FRAME row (
docs/release/1.0-plan.md:142, ROADMAPW2): a frame registry, the v7envelope, the
read_frame*/describe_framefamily, and theFrameFaultclasses. It absorbs FRE-4,TAG-2 and ROB-8's
fault_classnaming.Frame versions are now named in one place —
weft_physical_type::frame_registry:FRAME_SINGLE_BLOCK = 5,FRAME_PAGED = 6,FRAME_ENVELOPE = 7,FRAME_VERSION_MAX_KNOWN = 7—with the rule in the module doc that a number is never reused or renumbered once it has shipped.
The envelope
wrap_frame_envelopeputs a header and a trailer around an existing frame and stores that frameverbatim, its own magic, version, body and CRC intact:
16 bytes of overhead (12-byte header with no extensions, plus the trailer). That is a deliberate
constraint:
weftdb/src/types/segment_store.rs:7022promises that an unconfigured deployment'sbytes are byte-for-byte what previous versions wrote, and the existing byte-pinning tests
(
weftseg.rs:4153-4175,4321-4341,segment_store.rs:117,154,196,217,381,2863,4721,…,weft-server/src/ingest.rs:602,835, which put the frame version on the HTTP surface) stay greenbecause a v5 or v6 body is untouched. A legacy reader pointed at an envelope still says
UnsupportedVersion { found: 7 }rather than misparsing it.An extension is
{id, critical, payload}. Unknown critical extension ⇒ the frame is refused:the writer produced that body assuming the extension would be applied, so continuing would serve
data under a contract nobody honoured. Unknown optional extension ⇒ ignored.
KNOWN_EXTENSIONSis empty on purpose — the envelope ships with no extensions, and the numbers area permanent namespace, so inventing one before a feature needs it costs a future release its only
free slot.
Reading without knowing the layout
describe_frame(metadata, no decode;Okfor a version this build does not know, withlayout: None),frame_layout_of(a header-only routing probe),unwrap_frame_envelope, andread_frame_segment/read_frame_paged_segment/read_frame_point/read_frame_points/read_frame_range/read_frame_range_physical.frame_layout_ofrather thandescribe_framefor routing, on purpose: it reads magic + version(and, for 7, the
body_version) and nothing else.describe_framewould pay a second CRC pass onevery read and would replace the reader's error sentence with its own. The chosen reader still
verifies every byte of the frame.
Fault classes
frame_fault_of(&WeftSegError) -> FrameFault—IntegrityorUndecodable { newer_format }, withfault_class()giving the stable stringsintegrity/newer_format/undecodable(plan C5,docs/release/1.0-plan.md:176).UnsupportedVersionis compared againstFRAME_VERSION_MAX_KNOWN, so a frame from a newer build is distinguished from one that is damaged;CodecNotEnabledand the newUnknownCriticalExtensionareundecodablebut notnewer_format, because the file is not ahead of us, our features are off.WeftSegErroris now#[non_exhaustive]and gainsUnknownCriticalExtension { id }andEnvelopeBodyMismatch { declared, found }.The store
SegmentStorerouted decodes bydescriptor.format_version == PAGED_SEGMENT_FORMAT_VERSION, i.e.by what the index row claimed. Anything that was not
6— including theunwrap_or(0)insegment_index.rs:433— went to the single-block decoder. It now asks the file: the 6 call sites(
:292,:1924,:3067,:3203,:3241,:4265) go throughweftdb/src/types/frame_read.rs,which probes the header, calls the matching
read_frame_*, and returns failures asweftdb::FrameFaultError(public, in theanyhowchain) under the sentencedecoding paged segment <path>: …/decoding segment <path>: …unchanged, so nothing thatmatched those strings keeps working differently.
HTTP mapping, at
weft-server'sclassify_typed:Undecodable { newer_format: true }⇒503 incompatible_store, every other frame fault ⇒500 frame_undecodable, with the class indetails.fault.Code::FrameUndecodablewas added by #90 with a wire sentence and noproducer; this is the producer.
Not included
No writer opts into v7 — seals still write 5 and 6. This slice ships the mechanism and the classes;
the v7 opt-in and the C5 quarantine/
Weft-Incompletebehaviour belong to D-S12/TAG-9 and the ingestlanes. Frame naming (
{enc(aspect)}~g{gen}~p{prec}.weftseg, the 200-byte stem cap and the~h{hash}cut) is already on main inweftdb/src/types/frame_name.rsand is untouched here.Testing
weft-physical-type:cargo +stable test -p weft-physical-type --lib— 306 passed, of which 14are new: envelope round-trip, a legacy reader refusing an envelope with
UnsupportedVersion,critical-extension refusal, optional-extension ignore, count/header disagreement caught by CRC,
describe_framereporting a future version,frame_fault_ofclassification, and thelayout-aware read family over a wrapped frame.
weftdb:cargo +stable test -p weftdb --lib frame_read— 6 passed (the crate's lib suite is 395).New tests in
weftdb/src/types/frame_read.rscover routing through an envelope,FrameFault::Integrityon a flipped byte,
newer_formaton a rewritten version (CRC re-stamped, so the checksum is not whatcomplains), a forged newer-body envelope, critical-extension refusal through the store, and the
decoding paged segment …sentence with the fault in the chain.weft-server:cargo +stable test -p weft-server --test frame_fault— 2 passed. The C5 mapping isasserted end to end over a real sealed frame: a flipped byte in the middle of a
.weftseganswers500 frame_undecodablewithdetails.fault == "integrity", and a version byte pair rewritten pastFRAME_VERSION_MAX_KNOWNwith the trailer CRC re-stamped answers503 incompatible_storewithdetails.fault == "newer_format". The body carries the fixed X-ERR sentence, not the path or thedecoder's complaint.
by
persist/persist_paged).cargo +stable check --workspace --all-targets --locked,cargo +stable clippy --workspace --all-features --all-targets,RUSTDOCFLAGS="-D warnings" cargo +stable doc --workspace --no-deps --locked,cargo +nightly fmt --all -- --check.Review notes
read_bound_framestill checksbytes.len() == descriptor.byte_lenand the boundframe_crcagainst the file's last four bytes. Under an envelope those bytes are the envelope's CRC, which
is the right thing to compare only while nothing writes envelopes; when a writer opts in, that
binding has to name the inner frame's CRC. Flagged in the code where it happens.
weft-server/tests/*cannot fabricate a frame fault any more than before, becauseWeftSegErroris#[non_exhaustive]andweftdb::FrameFaultErrorhas no public constructor: anend-to-end status test has to corrupt a real file on disk, which is what
weft-server/tests/frame_fault.rsdoes. That is deliberate — a test cannot invent a status byconstructing a fault it likes.
format_versionin the index row no longer changes behaviour, because the row is not asked.It is still recorded and still reported by the descriptor.