@@ -686,3 +686,190 @@ does NOT address:
686686| Rust
687687| Makes typed-wasm a TypedQLiser target language
688688|===
689+
690+ == Wire-Level Carrier Sections
691+
692+ The post-codegen verifier (`crates/typed-wasm-verify/`) re-enforces a
693+ subset of these levels on emitted wasm bytes. To do that without
694+ re-parsing source, producers emit small custom sections — wasm's
695+ extension mechanism — that carry just enough of the source-level type
696+ information for the verifier to reconstruct the relevant judgments.
697+
698+ All carrier sections are independently optional from the verifier's
699+ perspective: absence is "no claim made," not "claim of compliance." A
700+ producer that emits only `typedwasm.ownership` is conforming with
701+ respect to L7 / L10 but makes no L2-L6 / L15 claims that the verifier
702+ can re-check.
703+
704+ [cols="1,1,1,2"]
705+ |===
706+ | Section | Covers | Status (2026-05) | Codec entry point
707+
708+ | `typedwasm.ownership`
709+ | L7 aliasing, L10 linearity
710+ | Shipped; canonical wire format
711+ | `parse_ownership_section_payload` / `build_ownership_section_payload`
712+
713+ | `typedwasm.regions`
714+ | L2 region-binding, L3 type-compatible access, L4 null-safety, L5 bounds-proof, L6 result-type
715+ | Proposal 0001 `[review]`; codec behind `unstable-l2`
716+ | `parse_regions_section_payload` / `build_regions_section_payload`
717+
718+ | `typedwasm.capabilities`
719+ | L15 resource capabilities (lattice + per-function requirements; excluding L15-C per-call-site grants)
720+ | Proposal 0001 `[review]`; codec behind `unstable-l15`
721+ | `parse_capabilities_section_payload` / `build_capabilities_section_payload`
722+
723+ | `typedwasm.access-sites`
724+ | L2 enforcement on emitted bytes (per-instruction `(func_idx, byte_offset) -> (region_id, field_id)` mapping)
725+ | Proposal 0002 `[review]`; codec behind `unstable-l2`
726+ | `parse_access_sites_section_payload` / `build_access_sites_section_payload`
727+ |===
728+
729+ === `typedwasm.ownership` (L7 / L10)
730+
731+ Wire format (little-endian, byte-aligned):
732+
733+ ----
734+ u32le count
735+ for each entry:
736+ u32le func_idx
737+ u8 n_params
738+ u8[] param_kinds (0=Unrestricted, 1=Linear, 2=SharedBorrow, 3=ExclBorrow)
739+ u8 ret_kind
740+ ----
741+
742+ The verifier reconstructs each function's ownership-annotated signature
743+ and checks the body against it for L7 (no aliasing of `ExclBorrow`) and
744+ L10 (`Linear` consumed exactly once on every code path). The 4-kind
745+ enum (`OwnershipKind`) is fixed at v1.
746+
747+ Lenient on truncation: a short read yields zeros for the missing
748+ bytes (interpreted as `Unrestricted` kinds and `func_idx = 0`). This
749+ defence-in-depth choice preserves cross-implementation parity with
750+ the OCaml reference parser in AffineScript's `lib/tw_verify.ml`.
751+
752+ === `typedwasm.regions` (L2-L6)
753+
754+ Wire format (little-endian, byte-aligned, lenient on truncation):
755+
756+ ----
757+ u16le version (= REGIONS_SECTION_VERSION = 1)
758+ u32le region_count
759+ for each region (in index order, 0..region_count-1):
760+ u32le name_len
761+ u8[] name (UTF-8, no NUL terminator)
762+ u32le field_count
763+ for each field (in declaration order):
764+ u32le field_name_len
765+ u8[] field_name (UTF-8)
766+ u8 kind (0=Scalar, 1=PtrOwning, 2=PtrBorrow, 3=PtrExclusive)
767+ u8 wasm_ty (0..10 = U8..WBool, 0xFF = N/A for ptr kinds)
768+ u32le target_region (0xFFFFFFFF if Scalar; else index into region table)
769+ u8 nullability (0=NonNull, 1=Nullable)
770+ u32le cardinality (1=single, n>1=fixed array, 0=unbounded/dynamic)
771+ u32le region_byte_size (sum-check; verifier may cross-check)
772+ ----
773+
774+ Maps to Idris2 spec types: `region_count` + region table → `List Region`
775+ (lookup-by-index); `field_count` + field table → `Schema` per region;
776+ `wasm_ty` → `WasmType` (11 variants); `kind` → `PtrKind` extended with
777+ `Scalar`; `nullability` → `Nullability`; `cardinality` feeds
778+ `InBounds idx count` for L5; `target_region` is the foreign-key for L2.
779+
780+ v0 producer surface narrowness — across AffineScript + ephapax, no
781+ producer emits v128/SIMD / 128-bit ints / reference types /
782+ variable-cardinality fields. See proposal 0001 §"v0 producer surface"
783+ for the full reserved-codepoint enumeration.
784+
785+ === `typedwasm.capabilities` (L15)
786+
787+ Wire format (little-endian, byte-aligned):
788+
789+ ----
790+ u16le version (= CAPABILITIES_SECTION_VERSION = 1)
791+ u32le capability_count
792+ for each capability (in index order, 0..capability_count-1):
793+ u32le name_len
794+ u8[] name (UTF-8)
795+ u32le function_count
796+ for each function:
797+ u32le func_idx (index into wasm function section)
798+ u32le required_count
799+ u32le[required_count] required_capability_indices
800+ (indices into capability table above;
801+ MUST be strictly increasing → trivially
802+ encodes ResourceCapabilities.DistinctCaps)
803+ ----
804+
805+ Maps to `ResourceCapabilities.idr`: the flat capability table is
806+ `CapabilitySet`; strictly-increasing `required_capability_indices`
807+ structurally encodes `DistinctCaps`; per-function indices are the raw
808+ data the verifier feeds to `ContainedIn`.
809+
810+ L15-C (call-graph monotonicity / `CallCompatible`) is deferred to a
811+ v1.4.x follow-up section `typedwasm.capability-grants` — see
812+ typed-wasm#96.
813+
814+ The codec's parser defensively sorts and dedups `required` indices on
815+ read, so a future producer that regresses on the producer obligation
816+ still yields a `DistinctCaps`-conforming structure to downstream
817+ verification.
818+
819+ === `typedwasm.access-sites` (L2 enforcement)
820+
821+ Wire format (little-endian fixed-width header, LEB128 per entry):
822+
823+ ----
824+ u16le version (= ACCESS_SITES_SECTION_VERSION = 1)
825+ u32_leb128 entry_count
826+ for each entry (in producer-emission order):
827+ u32_leb128 func_idx
828+ u32_leb128 instruction_byte_offset (within function body, post-codegen,
829+ post any wasm-opt rewrite)
830+ u32_leb128 region_id (index into typedwasm.regions table)
831+ u32_leb128 field_id (index into target region's field table)
832+ ----
833+
834+ Encoding B (LEB128 per field) per proposal 0002 measurement: ~5
835+ B/entry on average, ~1.1% module overhead at the 6-fixture spec scale.
836+ Encoding A (flat 4× `u32le`, 16 B/entry, ~2.9% overhead) was rejected;
837+ encoding C (per-function batched delta offsets, ~3.5 B/entry,
838+ substantially more complex codec) is deferred to a possible v2.
839+
840+ Entries are NOT required to be sorted in v1; a future v2 MAY require
841+ sorted-by-`(func_idx, offset)` to enable binary search. v1 consumers
842+ build a `HashMap<(u32, u32), (u32, u32)>` at parse time.
843+
844+ The verifier resolves a typed access by looking up the entry in the
845+ access-sites map, then cross-referencing `region_id` + `field_id`
846+ against `typedwasm.regions`. Without this section, L2-the-enforcement
847+ (every typed access resolves to a declared region/field) cannot fire
848+ on emitted bytes even though L2-the-spec (regions/fields exist;
849+ cross-module schema agreement) is fully covered by
850+ `typedwasm.regions`.
851+
852+ `MissingDependentCarrier` semantics: emitting `typedwasm.access-sites`
853+ without `typedwasm.regions` is a hard error — the access-sites entries
854+ reference region/field indices that no companion table resolves.
855+
856+ === Versioning policy
857+
858+ `typedwasm.ownership` is unversioned (the original carrier; renamed
859+ from `affinescript.ownership` in PR #65). The three carriers added by
860+ proposals 0001 + 0002 all begin with `u16le version`. Forward-compat
861+ contract:
862+
863+ * *Additive* changes (new trailing fields per record) ship as the same
864+ major version; the lenient reader silently ignores the new bytes on a
865+ v1 consumer.
866+ * *Breaking* changes bump the version and the verifier rejects unknown
867+ versions with a fresh `*SectionError`-style variant (per-section).
868+
869+ === Cross-references
870+
871+ * Codec: `crates/typed-wasm-verify/src/section.rs`
872+ * Proposal 0001 (regions + capabilities): `docs/proposals/0001-multi-producer-carrier-section.adoc`
873+ * Proposal 0002 (access-sites): `docs/proposals/0002-access-site-carrier.adoc`
874+ * Pre-stage roadmap: typed-wasm#106 (`[review]` → `[accepted]` tracker)
875+ * Open questions: typed-wasm#94 (WBool wire width), #95 (region-imports v0.3), #96 (capability-grants L15-C), #97 (producer-readiness checklist)
0 commit comments