Expand description
The compiled slice of one component, in the form a client can execute — Mode B’s payload.
docs/05-tier-lowering.md §5.1: a Mode-B component
is “the component’s pure code compiled to WASM … fine-grained signal graph, local speculative
fold + seq-based reconciliation”. This module is the payload half of that: what a browser
has to be given before it can render the page and guess the next state for itself.
Four roles cross, and each is here for a reason a Mode-A client does not have:
view— because in Mode B the browser renders. This is the only per-component role; the other three belong to the application, which is why two components of one program share everything but this.validateandfold— because optimism is not a trick. “The browser applies the expected event to its local copy speculatively — legitimate because it runs the same pure fold the server runs” (docs/10-decisions.mdD5). The sameCore, not a second implementation of it.init— so a client that has never spoken to the server has a state to render, which is what an offline cold start is (D7).
Plus the definitions those four reach, transitively: Core refers to top-level definitions
by name (CoreKind::Global), so a role without them is not executable. That closure is the
component’s slice, and it is why a bundle is smaller than a program.
§What a bundle is not
It is not a program. There is no Placed, no signal graph, no type table, no test — nothing
the client would need only in order to check something, because the client checks nothing.
The program was checked on the way in; the bundle is what is left when the only remaining
question is how to run it.
§Why a mirror type rather than #[derive(Serialize)] on Core
crate::repr’s argument, one layer up: a concrete type is where the decisions about what
crosses become visible and reviewable rather than implied by whatever fields a struct happens
to have. Two decisions are taken here, and neither should be able to change by accident:
- Types are dropped. A
Corenode carries theTythe checker inferred, and the evaluator never reads it — it dispatches on values. Carrying resolved types would roughly double the payload to say something the only consumer cannot use. A compiling client backend would need them, and that is a format version rather than a field somebody adds quietly:FORMATis checked on load. - Spans are kept. They are three integers and they are the difference between “the fold
failed” and “the fold failed at
todo.beck:47” in a browser console.
§Why the bundle names the compiler that made it
Prim is encoded as its position in the primitive table, which is the compact encoding —
and which silently means something different if the table changes. So a bundle carries
shape_id: a digest of every primitive’s name and number. A kernel built from a different
compiler refuses the bundle instead of executing a str_len that used to be a list_len. It
is the same rule crate::repr’s FORMAT states for the log — “a misread log is worse than
an unreadable one” — applied to code rather than to data.
Structs§
- Bundle
- A component’s slice: the code, and nothing else.
Enums§
- BadBundle
- Why a bundle could not be read.
Constants§
- FORMAT
- The bundle format version, stamped into every bundle and checked on load.
Functions§
- shape_
id - A digest of the primitive table: every primitive’s name and the number this compiler gives it.