## PR-03 — Frame model v0: locals, operand stack, and function metadata
**Why:** `let x: int = 1` failing usually means locals/frames are not modeled correctly.
### Scope
* Define `FunctionMeta`:
* `code_offset`, `code_len`
* `param_slots`, `local_slots`, `return_slots`
* `max_stack_slots` (computed by verifier or compiler)
* Define `Frame`:
* `base` (stack base index)
* `locals_base` (or equivalent)
* `return_slots`
* `pc_return`
* Decide representation:
* Option A (recommended v0): **single VM stack** with fixed layout per frame:
* `[args][locals][operand_stack...]`
* Use `base + local_index` addressing.
### Deliverables
* `CallStack` with `Vec`
* `enter_frame(meta)` allocates locals area (zero-init)
* `leave_frame()` reclaims to previous base
### Tests
* locals are isolated per call
* locals are zero-initialized
* stack is restored exactly after return
### Acceptance
* Locals are deterministic and independent from operand stack usage.
---
## PR-04 — Locals opcodes: GET_LOCAL / SET_LOCAL / INIT_LOCAL
**Why:** PBS `let` and parameters need first-class support.
### Scope
* Implement opcodes:
* `GET_LOCAL ` pushes value slots
* `SET_LOCAL ` pops value slots and writes
* `INIT_LOCAL ` (optional) for explicit initialization semantics
* Enforce bounds: local slot index must be within `[0..param+local_slots)`
* Enforce slot width: if types are multi-slot, compiler emits multiple GET/SET or uses `*_N` variants.
### Deliverables
* `LocalAddressing` utilities
* Deterministic trap codes:
* `TRAP_INVALID_LOCAL`
* `TRAP_LOCAL_WIDTH_MISMATCH` (if enforced)
### Tests
* `let x: int = 1; return x;` works
* invalid local index traps
### Acceptance
* `let` works reliably; no stack side effects beyond specified pops/pushes.
---
## PR-05 — Core arithmetic + comparisons in VM (int/bounded/bool)
**Why:** The minimal executable PBS needs arithmetic that doesn’t corrupt stack.
### Scope
* Implement v0 numeric opcodes (slot-safe):
* `IADD, ISUB, IMUL, IDIV, IMOD`
* `ICMP_EQ, ICMP_NE, ICMP_LT, ICMP_LE, ICMP_GT, ICMP_GE`
* `BADD, BSUB, ...` (or unify with tagged values)
* Define conversion opcodes if lowering expects them:
* `BOUND_TO_INT`, `INT_TO_BOUND_CHECKED` (trap OOB)
### Deliverables
* Deterministic traps:
* `TRAP_DIV_ZERO`
* `TRAP_OOB` (bounded checks)
### Tests
* simple arithmetic chain
* div by zero traps
* bounded conversions trap on overflow
### Acceptance
* Arithmetic and comparisons are closed and verified.
---
## PR-06 — Control flow opcodes: jumps, conditional branches, structured “if”
**Why:** `if` must be predictable and verifier-safe.
### Scope
* Implement opcodes:
* `JMP `
* `JMP_IF_TRUE `
* `JMP_IF_FALSE `
* Verifier rules:
* targets must be valid instruction boundaries
* stack height at join points must match
### Tests
* nested if
* if with empty branches
* branch join mismatch rejected
### Acceptance
* Control flow is safe; no implicit stack juggling.
---
## PR-07 — Calling convention v0: CALL / RET / multi-slot returns
**Why:** Without a correct call model, PBS isn’t executable.
### Scope
* Introduce `CALL `
* caller pushes args (slots)
* callee frame allocates locals
* Introduce `RET`
* callee must leave exactly `return_slots` on operand stack at `RET`
* VM pops frame and transfers return slots to caller
* Define return mechanics for `void` (`return_slots=0`)
### Deliverables
* `FunctionTable` indexing and bounds checks
* Deterministic traps:
* `TRAP_INVALID_FUNC`
* `TRAP_BAD_RET_SLOTS`
### Tests
* `fn add(a:int,b:int):int { return a+b; }`
* multi-slot return (e.g., `Pad` flattened)
* void call
### Acceptance
* Calls are stable and stack-clean.
---
## PR-08 — Host syscalls v0: stable ABI, multi-slot args/returns
**Why:** PBS relies on deterministic syscalls; ABI must be frozen and enforced.
### Scope
* Unify syscall invocation opcode:
* `SYSCALL `
* Runtime validates:
* pops `arg_slots`
* pushes `ret_slots`
* Implement/confirm:
* `GfxClear565 (0x1010)`
* `InputPadSnapshot (0x2010)`
* `InputTouchSnapshot (0x2011)`
### Deliverables
* A `SyscallRegistry` mapping id -> handler + signature
* Deterministic traps:
* `TRAP_INVALID_SYSCALL`
* `TRAP_SYSCALL_SIG_MISMATCH`
### Tests
* syscall isolated tests
* wrong signature traps
### Acceptance
* Syscalls are “industrial”: typed by signature, deterministic, no host surprises.
---
## PR-09 — Debug info v0: spans, symbols, and traceable traps
**Why:** Industrial debugging requires actionable failures.
### Scope
* Add optional debug section:
* per-instruction span table (`pc -> (file_id, start, end)`)
* function names
* Enhance trap payload with debug span (if present)
### Tests
* trap includes span when debug present
* trap still works without debug
### Acceptance
* You can pinpoint “where” a trap happened reliably.
---
## PR-10 — Program image + linker: imports/exports resolved before VM run
**Why:** Imports are compile-time, but we need an industrial linking model for multi-module PBS.
### Scope
* Define in bytecode:
* `exports`: symbol -> func_id/service entry (as needed)
* `imports`: symbol refs -> relocation slots
* Implement a **linker** that:
* builds a `ProgramImage` from N modules
* resolves imports to exports
* produces a single final `FunctionTable` and code blob
### Notes
* VM **does not** do name lookup at runtime.
* Linking errors are deterministic: `LINK_UNRESOLVED_SYMBOL`, `LINK_DUP_EXPORT`, etc.
### Tests
* two-module link success
* unresolved import fails
* duplicate export fails
### Acceptance
* Multi-module PBS works; “import” is operationalized correctly.
---
## PR-11 — Canonical integration cartridge + golden bytecode snapshots
**Why:** One cartridge must be the unbreakable reference.
### Scope
* Create `CartridgeCanonical.pbs` that covers:
* locals
* arithmetic
* if
* function call
* syscall clear
* input snapshot
* Add `golden` artifacts:
* canonical AST JSON (frontend)
* IR Core (optional)
* IR VM / bytecode dump
* expected VM trace (optional)
### Tests
* CI runs cartridge and checks:
* no traps
* deterministic output state
### Acceptance
* This cartridge is the “VM heartbeat test”.
---
## PR-12 — VM test harness: stepper, trace, and property tests
**Why:** Industrial quality means test tooling, not just “it runs”.
### Scope
* Add `VmRunner` test harness:
* step limit
* deterministic trace of stack deltas
* snapshot of locals
* Add property tests (lightweight):
* stack never underflows in verified programs
* verified programs never jump out of bounds
### Acceptance
* Debugging is fast, and regressions are caught.
---
## PR-13 — Optional: Refactor Value representation (tagged slots) for clarity
**Why:** If current `Value` representation is the source of complexity/bugs, refactor now.
### Scope (only if needed)
* Make `Slot` explicit:
* `Slot::I32`, `Slot::I64`, `Slot::U32`, `Slot::Bool`, `Slot::ConstId`, `Slot::GateId`, `Slot::Unit`
* Multi-slot types become sequences of slots.
### Acceptance
* Simpler, more verifiable runtime.
---
# Work split (what can be parallel later)
* VM core correctness: PR-01..PR-08 (sequential, contract-first)
* Debug + tooling: PR-09, PR-12 (parallel after PR-03)
* Linking/imports: PR-10 (parallel after PR-01)
* Canonical cartridge: PR-11 (parallel after PR-05)
---
# “Stop the line” rules
1. If a PR introduces an opcode without stack spec + verifier integration, it’s rejected.
2. If a PR changes bytecode layout without bumping version, it’s rejected.
3. If a PR adds a feature before the canonical cartridge passes, it’s rejected.
---
# First implementation target (tomorrow morning, start here)
**Start with PR-02 (Opcode spec + verifier)** even if you think you already know the bug.
Once the verifier exists, the rest becomes mechanical: every failure becomes *actionable*.
## Definition of Done (DoD) for PBS v0 “minimum executable”
A single canonical cartridge runs end-to-end:
* `let` declarations (locals)
* arithmetic (+, -, *, /, %, comparisons)
* `if/else` control flow
* `when` expression (if present in lowering)
* function calls with params + returns (including `void`)
* multiple return slots (flattened structs / hardware value types)
* host syscalls (e.g., `GfxClear565`, `InputPadSnapshot`, `InputTouchSnapshot`)
* deterministic traps (OOB bounded, invalid local, invalid call target, stack underflow)