Skip to content

spec(L2): pin WBool wire width at 4 bytes (closes #94)#113

Merged
hyperpolymath merged 1 commit into
mainfrom
wbool-wire-width-pin
May 30, 2026
Merged

spec(L2): pin WBool wire width at 4 bytes (closes #94)#113
hyperpolymath merged 1 commit into
mainfrom
wbool-wire-width-pin

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Closes #94 — the only remaining open question on proposal 0001 before [review] → [accepted] flip.

Decision: WBool is 4 bytes on the wire (i32.store/load; 0 = false, nonzero = true). The narrower 1-byte encoding is reserved for a future WBoolPacked variant in v2.

Two changes

File Change
docs/proposals/0001-multi-producer-carrier-section.adoc New §"WBool wire width — pinned at 4 bytes" subsection (~36 LOC) inside §"Wire format — typedwasm.regions". Documents rationale, reserves WBoolPacked v2, references the rejected lenient alternative.
src/abi/TypedWasm/ABI/Region.idr sizeOf WBool = 4 (was 1) with inline comment pointing at the proposal text and producer-side evidence files.

Why 4-byte, not 1-byte, not mixed-width

  1. Producer reality: both shipping producers already emit 4-byte i32.store for boolean fields (AffineScript lib/codegen.ml:180-190+:373; Ephapax src/ephapax-wasm/src/lib.rs:2937-2944). Pinning at 4 = zero codegen changes.
  2. Spec self-consistency: Region.idr was the outlier at 1 byte; this PR brings spec into alignment with producer reality. alignOf = sizeOf (Region.idr:113) carries through to field-alignment math. No proofs or tests hardcode WBool=1 — verified by grep.
  3. Verifier simplicity: single canonical width means no new MixedWidthBoolean error variant; region_byte_size cross-check is deterministic per schema.
  4. Future flexibility: a 1-byte WBoolPacked (or named-equivalent) remains the v2 escape hatch as a new WasmType constructor, NOT a width flag on the existing WBool codepoint. Additive, not breaking.

Rejected: lenient mixed-width

The §A sketch in #94's recommendation (allow either width per field, validate consistency) is rejected because:

  • region_byte_size becomes producer-choice-dependent — same schema → different schemaSize values
  • Forces a new MixedWidthBoolean verifier error variant for marginal flexibility
  • Complicates cross-module schema agreement: a 1-byte WBool field in one module is structurally incompatible with a 4-byte WBool field of the same name in another module

Risk assessment

  • Idris2 sizeOf change cascades to schemaSize / computeOffsets / alignOf — none of these is hardcoded against WBool=1 in any test or proof. MultiModule.idr's Example schema uses WBool but only via SchemaEq/SchemaSub structural relations.
  • TypedAccess.idr:51 (HostType WBool = Bool) is host-side type mapping; unaffected by wire width.
  • crates/typed-wasm-verify region_byte_size validation is currently MAY cross-check (not enforced) — when implemented later, the canonical 4-byte rule makes the check deterministic.

Acceptance update for #94

  • Proposal 0001 amended with explicit WBool wire width.
  • [n/a] No MixedWidthBoolean error variant (strict resolution chosen).
  • Forward-pointer to WBoolPacked future-extension added.

What this means for 0001 [review] → [accepted]

This was the last load-bearing open question on proposal 0001. After merge:

Acceptance gate State
Criterion 1 (producer signoff)
Criterion 2 (wire↔spec mapping) ✅ Appendix A (PR #110)
Criterion 3 (draft codec) ✅ PR #107
Criterion 4 (draft spec doc) ✅ PR #108
Criterion 5 (cross-repo issues filed) ✅ affinescript#444 + ephapax#221
OQ #94 (WBool width) this PR
OQ #95 (region-imports) ✅ proposal 0003 [draft] (PR #112)
OQ #96 (capability-grants) ✅ proposal 0004 [draft] (PR #111)

Proposal 0001 is fully ready for [accepted] after this lands — owner decision is the only remaining step.

Test plan

  • git grep confirms no hardcoded WBool=1 size assertions in proofs or tests.
  • sizeOf WBool only used via computeOffsets/alignOf/fieldSize (derived, not hardcoded).
  • Idris2 build passes (CI will verify).
  • Cargo build + test passes (no Rust code changes — should be a no-op).

Related

🤖 Generated with Claude Code

Resolves typed-wasm#94 by amending proposal 0001 with an explicit
"WBool wire width" subsection and updating the Idris2 spec to match.

## Decision

WBool occupies 4 bytes on the wire, accessed via wasm's standard
i32.store / i32.load opcodes (0 = false, nonzero = true). Producers
SHALL NOT emit i32.store8 / i32.load8 for WBool fields in v1 — the
narrower encoding is reserved for a future WBoolPacked variant in
v2 (separate wasm_ty codepoint, NOT a width flag on WBool).

## Why 4-byte, not 1-byte or mixed-width

1. **Producer reality.** Both shipping producers already emit 4-byte
   i32.store for boolean fields:
   - AffineScript: lib/codegen.ml:180-190 (type_to_wasm: Bool → I32)
     + :373 (LitBool emits I32Const + I32Store)
   - Ephapax: src/ephapax-wasm/src/lib.rs:2937-2944
     (ty_to_valtype: BaseTy::Bool → ValType::I32)
   Pinning at 4 bytes matches every shipping producer without codegen
   changes.

2. **Spec self-consistency.** Region.idr's sizeOf WBool = 1 was the
   outlier; producers and proposal-implied semantics both assumed 4.
   This commit catches the spec up to producer reality. alignOf =
   sizeOf (Region.idr:113) carries the change through to field-
   alignment math (no proofs hardcode WBool=1; verified by grep).

3. **Verifier simplicity.** A single canonical width means no new
   MixedWidthBoolean error variant; region_byte_size cross-check is
   deterministic per schema.

4. **Future flexibility.** A 1-byte WBoolPacked (or named-equivalent)
   remains the v2 escape hatch for size-conscious producers —
   additive, not a wire-width flag on the existing WBool codepoint.

## Rejected alternative — lenient mixed-width

The "Recommendation §A" sketch in #94 (allow either 1 or 4 bytes
per field, validate consistency) is rejected because it:

- Makes region_byte_size producer-choice-dependent (same schema, two
  different schemaSize values depending on producer)
- Forces a new MixedWidthBoolean verifier error variant for marginal
  flexibility
- Complicates cross-module schema agreement (a 1-byte WBool field in
  one module is structurally incompatible with a 4-byte WBool field
  of the same name in another module — but SchemaEq currently
  ignores width since spec was at 1)

## Changes

- docs/proposals/0001-multi-producer-carrier-section.adoc: new
  §"WBool wire width — pinned at 4 bytes" subsection inside
  §"Wire format — typedwasm.regions" (~36 LOC). Documents rationale,
  reserves WBoolPacked v2, references the rejected alternative.

- src/abi/TypedWasm/ABI/Region.idr: `sizeOf WBool = 4` (was 1) with
  inline comment pointing at the proposal text and producer-side
  evidence files. No hardcoded WBool=1 assertions exist in proofs
  or tests (verified by grep across src/abi/ and crates/).

## Risk assessment

- Idris2 sizeOf change cascades to schemaSize / computeOffsets /
  alignOf. None of those is hardcoded against WBool=1 in any test or
  proof. The Example schema in MultiModule.idr uses WBool but only
  via SchemaEq/SchemaSub structural relations, not numeric size
  assertions.
- Region.idr's TypedAccess.idr usage (HostType WBool = Bool, line 51)
  is host-side type mapping, unaffected by wire width.
- crates/typed-wasm-verify region_byte_size validation is currently
  "MAY cross-check" (not enforced) — when implemented post-#94, the
  canonical 4-byte rule makes the check deterministic.

## Acceptance update for #94

- [x] Proposal 0001 amended with explicit WBool wire width.
- [n/a] No MixedWidthBoolean error variant (strict resolution chosen).
- [x] Forward-pointer to WBoolPacked future-extension added.

Refs: #34, #94, #106 (acceptance roadmap),
ephapax#165 §2-Q1, affinescript#402 §3-Q1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 96 issues detected

Severity Count
🔴 Critical 8
🟠 High 19
🟡 Medium 69

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in boj-build.yml",
    "type": "unknown",
    "file": "boj-build.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in c5-regenerate.yml",
    "type": "unknown",
    "file": "c5-regenerate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in cargo-audit.yml",
    "type": "unknown",
    "file": "cargo-audit.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in codeql.yml",
    "type": "unknown",
    "file": "codeql.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "unknown",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "unknown",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "unknown",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "unknown",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "unknown",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit 81a5af8 into main May 30, 2026
31 checks passed
@hyperpolymath hyperpolymath deleted the wbool-wire-width-pin branch May 30, 2026 15:01
hyperpolymath added a commit that referenced this pull request May 30, 2026
…loses #97) (#114)

## Summary

Closes [#97](#97) —
addresses both halves of the paired-review meander finding.

## (A) Producer-readiness checklist — Appendix B in proposal 0001 (~120
LOC)

Tabulates each carrier section's IR prerequisites, current producer-side
status, and roadmap-issue identifier in each producer's tracker where
the gap is named:

| Carrier | IR prerequisites | Status |
|---------|-----------------|--------|
| `typedwasm.ownership` | per-fn ownership-kind tracking | **SHIPPING**
|
| `typedwasm.regions` | region decls + field offsets + stable indices |
**NOT YET EMITTABLE** (AS Roadmap C1 deferred; Ephapax dead code) |
| `typedwasm.capabilities` | per-fn cap decl + module budget + effect IR
| **NOT YET EMITTABLE** (AS Roadmap C2 not started; Ephapax
Perform/Handle stubbed) |
| `typedwasm.region-imports` (0003) | + cross-module region tracking +
wasm-module-name resolution | **NOT YET EMITTABLE** |
| `typedwasm.access-sites` (0002) | + per-instr tracking + post-rewrite
hook | **NOT YET EMITTABLE** (depends on regions) |
| `typedwasm.capability-grants` (0004) | + per-callsite grant tracking |
**NOT YET EMITTABLE** (depends on capabilities) |

NOTE block surfaces the all-or-nothing implication: **do not emit a
section you cannot populate.**

Proposal 0002 gains a §"Producer-readiness (cross-reference)" subsection
(~28 LOC) pointing to 0001's Appendix B plus three access-sites-specific
items: per-instruction tracking, post-rewrite hook discipline,
region-index stability across sections.

## (B) Canonical emit ordering — extends 0001 §"Producer obligations"

New 5th item specifying the recommended emit order matching the runtime
dependency graph:

```
1. typedwasm.ownership
2. typedwasm.regions
3. typedwasm.region-imports        (proposal 0003)
4. typedwasm.capabilities
5. typedwasm.access-sites          (proposal 0002)
6. typedwasm.capability-grants     (proposal 0004)
```

**Consumers MUST NOT depend on this order** — the verifier reads
sections by custom-section name, so any order is parseable. The
canonical order exists for cross-producer module comparability
(byte-equal carrier sequences for two producers of the same logical
module — useful for `cmp` / `diff` / content-addressing).

## Acceptance update for #97

- [x] Proposal 0001 gains §"Producer-readiness checklist" appendix
listing the IR prerequisites for each section.
- [x] Proposal 0002 gains a one-line cross-reference (expanded to a full
subsection with access-sites-specific items) to 0001's checklist.
- [x] Proposal 0001 §"Producer obligations" specifies the canonical emit
order with a "consumer MAY accept any order" note.
- [ ] When AffineScript / Ephapax start their respective Roadmap
C1/C2/C3 implementations, those issues cross-reference this checklist.
*(Producer-side action, not a typed-wasm gate.)*

## What this closes

Together with #94, #95, #96 (resolved in PRs #110/#111/#112/#113), this
completes all the documentation-side gates around proposals 0001 and
0002. Both proposals are now fully ready for `[review] → [accepted]`
owner decision.

## Test plan

- [x] AsciiDoc renders cleanly (cols-tables, lower-alpha sub-list,
code-block fences).
- [x] Cross-reference link to Appendix B from proposal 0002 uses
AsciiDoc anchor format.
- [x] No code changes — no Cargo/Idris2 build required.

## Related

- #97 (this PR closes it)
- #34 (proposal 0001 umbrella)
- #106 (acceptance roadmap tracker)
- ephapax#165 §3+§6 / affinescript#402 §4+§7 — paired reviews surfacing
the gap
- affinescript#462 (access-sites codegen issue — referenced from
Appendix B)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 30, 2026
…115)

## Summary

Owner-decision close of the proposal-acceptance arc. Both proposals
satisfied every acceptance gate this session ([see #106 wrap-up
comment](#106 (comment)));
this PR applies the status flip.

## Status changes

- `docs/proposals/0001-multi-producer-carrier-section.adoc` — `review →
accepted`
- `docs/proposals/0002-access-site-carrier.adoc` — `review → accepted`
- `docs/proposals/README.adoc` — "Current proposals" table updated; NOTE
block clarifies files remain in `docs/proposals/` (ADR promotion is a
separate file-restructure follow-up).

## LEVEL-STATUS.md updates

| Level | Before | After |
|-------|--------|-------|
| L2 (region binding) | proposal-stage | **YES** (carrier-backed) —
`verify_access_sites_from_module` (PR #109); gated `unstable-l2` |
| L3–L6 (type-compat, null, bounds, result-type) | proposal-stage |
**YES** (carrier-backed, schema half) — regions codec PR #107;
per-access enforcement gated on producer codegen |
| L15-A/B (capabilities) | proposal-stage | **YES** (carrier-backed) —
`verify_capabilities_from_module` (PR #109); gated `unstable-l15` |
| L13 cross-module (positive form) | — | proposal-stage — proposal 0003
`[draft]` (new row) |

"Open gating items" section rewritten to remove obsolete pre-acceptance
gates (all shipped via #107/#109) and enumerate the remaining
producer-side prerequisites + [draft] proposal 0003/0004 work.

## What this DOES NOT change

- **File locations**: proposals stay in `docs/proposals/`. ADR promotion
to `docs/decisions/` (with renumbering — existing ADR 0001 is
"adopt-rsr-standard", so proposals 0001/0002 would become ADRs
0002/0003) is a deliberate separate PR to keep the status flip cleanly
auditable.
- **Code**: no source or test changes. Verifier passes already shipped
in #107/#109 behind `unstable-l2` / `unstable-l15` feature flags.
- **Producer-side codegen**: still pending per Appendix B prerequisites
in proposal 0001.

## Test plan

- [x] Status field flipped on both proposals.
- [x] README table reflects new statuses with date.
- [x] LEVEL-STATUS L2/L3-L6/L15 rows show carrier-backed YES.
- [x] Open gating items rewritten (4 items: producer codegen, L13
cross-module, L15-C, ADR promotion).
- [ ] CI passes (doc-only changes; no Cargo/Idris2 build risk).

## Related

- #34 (proposal 0001 umbrella)
- #78 (proposal 0002 RFC)
- #106 (acceptance roadmap tracker — full wrap-up comment)
- This session's 5-PR sprint that satisfied every gate: #107 (codec),
#108 (spec doc), #109 (verifier), #110 (Criterion 2 appendix), #111
(proposal 0004 draft for #96), #112 (proposal 0003 draft for #95), #113
(WBool pin for #94), #114 (producer-readiness for #97).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: pin WBool wire width in proposal 0001 (canonical 4-byte or lenient mixed-width validation)

1 participant