# Borz Language Specification

> *By the Agents · Through the Agents · For the Agents.*

**Version:** 0.9.5 (RFC-0001–0060 + RFC-BORZ-EXT-001..011 + RFC-DELIGHT-METER + RFC-DENSE-LAYOUT-V3) · **Status:** Authoritative · **Audience:** Humans + LLMs.

Borz is a **message-driven language for agentic distributed systems** — two
first-class constructs: **agent** (pure LLM persona, model-portable, from local
ollama to Claude) and **actor** (deterministic compiled code, four compile targets).
Actors lower to native Go binaries, DEMIX (Go + DEMIX SDK), and DENSE C++
nanoservices running inside KVM micro-VMs — with **no interpreter and no runtime VM**.

This document is the single source of truth for the surface language. It is
written to be ingested by LLMs in one pass: each section starts with the rule,
follows with a minimal example, then states the constraints.

---

## TL;DR for LLMs

If you are an LLM generating Borz code from scratch, the seven things you need
to know:

1. Indentation matters. Open a block with `:`, indent four spaces, dedent to
   close. No braces, no semicolons.
2. Programs declare three things at the top level: `msg` (data envelopes),
   `actor` (state + handlers), and optionally `type` (plain structs), `enum`,
   `trait`, `impl`.
3. State lives only inside actors. There are no globals, no closures, no
   inheritance, no shared variables.
4. Handlers run to completion on one message at a time. They return either by
   calling `response(...)`, `fail(...)`, or by falling off the end.
5. Messages cross actor boundaries with `Target <- Msg(...)`. Arguments are
   positional by default (C-style) or named with `field=value`.
6. Compile with `borz compile file.borz --target {native|demix|dense}`. Each
   target is a separate, fully native binary — no Borz VM runs at runtime.
7. When unsure: parenthesise expressions, split compound conditions, put
   aggregates first in `@persistent` blocks. The compiler's parser is
   intentionally simple.

---

## 1. Lexical Structure

| Element | Form |
|---------|------|
| comment | `# …` (rest-of-line) |
| identifier | `[A-Za-z_][A-Za-z0-9_]*` |
| string | `"…"` (Go-style escapes) |
| integer | decimal only, e.g. `1234` |
| boolean | `true` `false` |
| keywords | `actor msg type enum trait impl fn on on_start on_recover var let val if else match case for in every after within infer response fail log send spawn ret import use as @persistent @invariant` |
| operators | `+ - * / % == != < <= > >= = <- \|\| && !` |

Indentation is significant (Python-like). A block opens with `:` followed by a
newline + indent, and closes on dedent.

**Operator precedence** (RFC-0002 — Pratt parser, highest to lowest):

| Level | Operators | Associativity |
|-------|-----------|---------------|
| 8     | `()` call, `.` member, `[]` index, `?` try | left |
| 7     | unary `-` `!` | right |
| 6     | `*` `/` `%` | left |
| 5     | `+` `-` | left |
| 4     | `<` `<=` `>` `>=` | left |
| 3     | `==` `!=` | left |
| 2     | `&&` | left |
| 1     | `\|\|` | left |

`2 * a + 2 * b` is now `(2*a) + (2*b)` — no need to parenthesise for the
common arithmetic cases. Parentheses still accepted everywhere.

---

## 2. Types

### 2.1 Primitive types
| Type | Notes |
|------|-------|
| `i64` | Signed 64-bit integer. The default numeric type. |
| `i32` | Signed 32-bit integer. |
| `i8`  | Signed 8-bit integer. |
| `i16` | Signed 16-bit integer. |
| `u8`  | Unsigned 8-bit integer. |
| `u16` | Unsigned 16-bit integer. |
| `u32` | Unsigned 32-bit integer. |
| `u64` | Unsigned 64-bit integer. |
| `f64` | IEEE-754 double precision float. **Banned in `@deterministic` contract code.** |
| `bool` | `true` or `false`. |
| `str` | UTF-8 string. Up to 256 bytes in DENSE. |
| `bytes` | Variable-length binary payload (RFC-BORZ-EXT-001). On DENSE: state vars require `@dense(max_bytes=N)`; function-parameter / return positions lower to a generic `dense_bytes_t` view (RFC-BORZ-EXT-009). |
| `blob` | Alias for `bytes` used when streamed construction is expected. |

`i64` is the default integer type. `f64` is opt-in for fractional quantities.
Float literals are written as `3.14` or `1.0e-3`. For currency or any value
that must round predictably, prefer `Decimal` from `std/bignum` over `f64`.

**Binary payloads** — the `bytes` keyword unlocks the Ephernity ledger story:
content-addressed payloads, BLAKE3-hashed blocks, Falcon signatures. Use
`import "std/bytes" as *` to access constructors (`bytes_from_hex`,
`bytes_from_b64`, `bytes_concat2`), operations (`len_b`, `slice_b`,
`eq_b`, `to_hex_b`) and canonical little-endian put/get helpers
(`put_u32_le`, `get_u64_le`).

### 2.2 Collection types
| Form | Notes |
|------|-------|
| `List[T]` | Fixed-capacity list. `for x in xs:` iteration; `xs.append(x)`. |
| `Map[K, V]` | Fixed-capacity map. Partial support — prefer scalar fields. |

### 2.3 Enums — simple and sum types

**Simple enum** (no payloads):
```borz
enum Status:
    Pending
    Active
    Closed
```
Variants are `i64` constants (0, 1, 2, …). Compare and `match` on them.

**Sum type** (variants with payloads, RFC-0002):
```borz
enum Event:
    Tick
    UserJoin(user_id: i64)
    Error(code: i64, msg: str)
```
Construct: `Event.UserJoin(42)` or `Event.Error(500, "oops")`.
Match with destructuring:
```borz
match e:
    case Event.Tick:
        log "tick"
    case Event.UserJoin(uid):
        log i64_to_str(uid)
    case Event.Error(code, reason):
        log reason
    case _:
        log "other"
```
Match on a sum type is **exhaustiveness-checked** — the compiler errors if a
variant is missing and no `case _:` wildcard is present.

**Generic enum** (RFC-0002 Phase 6):
```borz
enum Option[T]:
    None
    Some(value: T)
```
Instantiate with `Option[i64]`. The compiler monomorphises each distinct instantiation.

**Stdlib sum types** — `std/option` and `std/result` ship pre-defined:
```borz
import "std/option" as *
import "std/result" as *
```

### 2.4 Structs — plain data types
```borz
type Point:
    x: i64
    y: i64
```
A `type` declaration introduces a plain product type with named fields.
Constructable positionally `Point(3, 4)` or by name `Point(x=3, y=4)`. Structs
have no methods or persistence by themselves — they are values you can store
in actor state vars or pass around as message fields.

**Generic struct** (RFC-0002 Phase 6):
```borz
type Pair[A, B]:
    first:  A
    second: B
```
Instantiate as `Pair[i64, str]`. The compiler monomorphises all instantiations.

### 2.5 Type aliases and newtypes (RFC-0002)

**Alias** — transparent synonym, no new nominal type:
```borz
type UserId = i64      # UserId and i64 are interchangeable
```

**Newtype** — distinct nominal type, same underlying layout:
```borz
newtype Cents = i64    # Cents ≠ i64; explicit conversion required
```
`Cents.of(2999)` constructs; `.value()` extracts. The compiler rejects implicit
`i64 → Cents` assignment.

### 2.6 Top-level free functions (RFC-0002 Phase 6)

Functions without a `self`/actor receiver can be declared at program top level:
```borz
fn add(x: i64, y: i64) -> i64:
    ret x + y

fn max_i64(a: i64, b: i64) -> i64:
    if a > b:
        ret a
    ret b
```
Called from handler bodies like any built-in function.

Generic free functions:
```borz
fn first[T](xs: List[T]) -> Option[T]:
    if xs.len() == 0:
        ret Option.None
    ret Option.Some(xs[0])
```

---

## 3. Messages

```borz
msg Transfer:
    from_id: i64
    to_id:   i64
    amount:  i64
```

Messages are pure data envelopes routed between actors. They have no methods.
Field order is significant in DENSE codegen (it determines binary layout) and
in positional construction.

A zero-field message is allowed:
```borz
msg Tick:
```

---

## 4. Actors

```borz
[@decorator ...]
actor ActorName:
    [@persistent] var field: type = literal
    val NAME:   type = literal     # constant

    on_start:
        <statements>

    on_recover:
        <statements>

    [@decorator ...]
    on msg MsgType:
        <statements>

    on fail MsgType:
        <statements>
```

* `var` — mutable actor state.
* `@persistent var` — state survives between invocations (JSON in native; full
  state region in DENSE/DEMIX).
* `val` — compile-time constant; cannot be reassigned.
* Initial values must be literals.
* Variable order does not affect correctness in DENSE layout v2 — see §10.

### Lifecycle hooks

| Hook | When it fires |
|------|---------------|
| `on_start` | Once at actor creation, before any handler runs. |
| `on_recover` | After supervisor restart, once, before resuming traffic. |
| `on msg M` | Each time a message of type `M` is delivered. |
| `on fail M` | If the handler for `M` failed or its watchdog fired. |
| `every D` | Periodic timer; placed at top level or inside `on_start`. |
| `after D` | One-shot timer; same placement rules. |

---

## 5. Handler Bodies

A handler is a list of statements indented under `on msg M:`. The message
arrives as the implicit `msg` variable: `msg.field`. There is no implicit
`return` — exit by calling `response(...)`, `fail(...)`, or by falling off the
end (returns no payload).

### 5.1 Statements at a glance
| Form | Description |
|------|-------------|
| `name = expr` | Assignment to state var or local. |
| `let name = expr` | Immutable local binding. |
| `var name: T` / `var name = T(...)` | Mutable local; struct or message form. |
| `if c:` / `else:` | Conditional. The condition must be one logical step. |
| `match v:` / `case p:` | Pattern match — enum variants, sum-type destructuring, literals. |
| `for x in xs:` | List iteration. |
| `every D:` / `after D:` | Timer block. |
| `within D:` | Run body under a deadline watchdog. |
| `response(k=v, ...)` | Emit reply to caller and exit handler. |
| `fail(code=N, msg="…")` | Emit error and exit handler. |
| `log(expr)` | Structured log line. |
| `ret expr` | Return a value from a free function or trait method body. |
| `Target <- Msg(...)` | Send a message to another actor. |
| `infer:` … | Call the embedded LLM client. See §8. |

**Match on simple enum:**
```borz
match status:
    case Status.Pending:
        log "waiting"
    case Status.Active:
        log "running"
    case _:
        log "other"
```

**Match on sum type (RFC-0002) — exhaustiveness checked:**
```borz
match event:
    case Event.Tick:
        log "tick"
    case Event.UserJoin(uid):
        log i64_to_str(uid)
    case Event.Error(code, msg):
        log msg
```
The compiler errors if a variant is missing and no `case _:` wildcard covers it.

### 5.2 Sends and message construction

Three forms, all valid. Choose the one that reads best:

```borz
# Positional, C-style — fields filled in declaration order.
Sink <- Coordinate(msg.x, msg.y, msg.z)

# Named — order-independent, self-documenting.
Sink <- Delta(dx=msg.x, dy=msg.y)

# Build first, send later — useful when the body assembles in stages.
let m = Delta(msg.total, msg.count)
Sink <- m

# Or step-by-step:
var m: Delta
m.dx = msg.dx + last_x
m.dy = msg.dy + last_y
Sink <- m
```

The same forms work for plain struct construction (`Point(3, 4)`, `Point(x=3,
y=4)`).

### 5.3 Mixing positional and named arguments

Named arguments always bind by name. Positional arguments bind to the *next*
field in declaration order — they do **not** automatically skip named slots.
Avoid mixing unless you have a clear convention; prefer all-positional or
all-named for any given call.

---

## 6. Traits — shared interfaces

```borz
trait Shape:
    fn area(self) -> i64
    fn perimeter(self) -> i64

impl Shape for Rectangle:
    fn area(self) -> i64:
        ret width * height

    fn perimeter(self) -> i64:
        let sum = width + height
        ret sum + sum
```

A `trait` declares method signatures. `impl Trait for Actor` provides bodies
for an actor. The actor's state vars are in scope inside method bodies as bare
identifiers (no `self.` prefix). Method bodies use `ret expr` to return values
(unlike handlers, which use `response`/`fail`).

In the native Go backend, each trait becomes a Go `interface`; each impl
becomes method receivers on the actor's state struct, so the state value
satisfies the interface.

Traits are not (yet) used for message dispatch — they are a way to share
shape-of-behaviour between actors and to plug into agentic frameworks that
expect typed contracts.

---

## 7. Annotations (Decorators)

Annotations attach metadata to declarations. They are `@name` or
`@name(key=val, ...)`.

| Annotation | Where | Effect |
|------------|-------|--------|
| `@persistent` | state var | Persist value across handler invocations. Optional attrs (RFC-BORZ-EXT-004): `backing="badger"\|"memory"\|"dense_512"`, `path="…"`, `write_freq="every_handler"\|"every_n_handlers"`, `n=N`, `ms=M`, `volatile=true`. |
| `@invariant` | actor | Compile-time + runtime assertion on state. |
| `@canonical` | type / struct | RFC-BORZ-EXT-003. Auto-derive deterministic, fixed-layout, cross-target encode/decode for the struct. Variants: `@canonical(layout="fixed"\|"length-prefixed"\|"versioned", current_version=N)`. |
| `@http(method="…", path="…")` | handler | Expose handler as HTTP endpoint in native target. |
| `@cli(command="…", desc="…")` | handler | Expose handler as CLI command in native target. |
| `@dense(max_items=N)` | actor | Fix DENSE memory layout cap. |
| `@dense(layout=3)` | actor | Opt in to DENSE state-layout v3 — per-page state region allocated on demand; closes the page-1+ visibility bug (RFC-DENSE-LAYOUT-V3). Auto-promoted when computed state size exceeds the page-0 safe margin (~3500 B). Magic `0xB0270003`, version `3`. |
| `@dense(max_bytes=N)` | `bytes` state var | Bound a variable-length `bytes` field for DENSE codegen. Lowers to `typedef struct { uint8_t data[N]; uint32_t len; } bytes_<N>_t;`. Overflow traps `__builtin_trap()` (`BYTES_OVERFLOW`). RFC-BORZ-EXT-001 + RFC-BORZ-EXT-009. |
| `@assert_state_size(max=N)` | actor | Reject compile if state grows beyond N bytes. |
| `@meter_budget(cpu_us=N, mem_kb=M, state_write_kb=K)` | actor | Per-call resource cap (RFC-DELIGHT-METER). Emitted into the DENSE state header as `k<Actor>_MeterBudgetCpuUs / MemKb / StateWriteKb` and into the `.meta` sidecar. DELIGHT enforces via KVM_RUN timeout + micro-VM mem bound + state-diff check. Defaults: 5000 µs / 64 KB / 4 KB. Over-budget calls trap and refund. |
| `@deterministic` | actor / handler | Ephernity contract code. **Enforced at compile time (RFC-BORZ-EXT-011).** Rejects: `wall_now_ms()` / `actor_clock_ms()` / `time.now*` (BANNED_CLOCK); `Map.iter()` / `for x in <map>:` without `iter_sorted()` (BANNED_ITER); any `f64` literal (BANNED_FLOAT); `random.*` (BANNED_RANDOM); cross-actor `<-` send (BANNED_SEND); inline `call go` / `call cpp` (BANNED_FFI); `infer:` step (BANNED_INFER); `every:` / `after:` timers (BANNED_CLOCK). |

### `pub` and `priv` modifiers (RFC-BORZ-EXT-007)

Any top-level declaration (`type`, `msg`, `actor`, `trait`, `fn`, `impl`,
`stage`) may carry a leading `pub` modifier. Today `pub` is a no-op
(public-by-default), but writing it now makes the source forward-compatible
with the package-boundary flip to private-by-default. `priv` already marks
a declaration as package-private.

Decorators on actors and handlers can stack:

```borz
@http(method="POST", path="/transfer")
@cli(command="transfer", desc="Move money")
on msg Transfer:
    ...
```

---

## 8. The `infer` Statement

```borz
infer:
    system: "You are a helpful classifier."
    user:   msg.text
    target: label          # state var receiving the LLM string output
```

Compiles to a synchronous call to the Anthropic API at runtime (native + DEMIX
targets). DENSE targets serialize the prompt across the substrate to a
host-side LLM proxy. The call is treated like any other handler step — it
blocks until the response arrives and is subject to the actor's `within`
watchdog if present.

---

## 9. Compilation Targets

| Flag | Output |
|------|--------|
| `--target native` | Single Go source file (`.native.go`) compilable to a static binary. CLI + HTTP modes built in. Suitable for laptops, edge devices, demos. |
| `--target demix` | Go + DEMIX SDK package. Suitable for replicated, supervised deployments inside a DEMIX cluster. |
| `--target dense --backend cpp` | One `_vm.cc` file per handler, compiled to flat ELF binaries. Each handler runs in its own KVM micro-VM. |
| `--target wasm` | WebAssembly module (experimental). |

The same `.borz` source compiles to all targets without changes (subject to
target-specific limitations noted inline in this spec).

---

## 10. Layout & Performance Notes

### DENSE State Layout v2

RFC-0004 shipped DENSE state layout v2.  The state region header is:

```
magic (4 bytes, 0xB0270001) | layout_version (2 bytes, = 2) | field_count (2 bytes)
```

Fields follow as `(tag u32, value bytes)` pairs **sorted by tag** so that field
offsets are compile-time constants.  Each `@persistent var` gets a stable 32-bit
tag = FNV-1a(`field_name + "_" + type_name`).  Field declaration order no longer
affects the binary layout — you can reorder fields freely without breaking
snapshots.

Adding a new `@persistent var` assigns a new tag; old snapshots load with that
field at its zero value (forward-compatible).  Removing a var is a deprecation
cycle: mark `@deprecated` and keep the tag for one release.

### EPT Visibility Fix

The KVM/EPT page-size mismatch bug (host writes past offset 4096 invisible to
the guest) is **fixed** as of RFC-0004:

* **Path A**: state region is now 2 MiB by default, matching the 2 MiB guest PDE.
* **Path B**: guest page tables use 4 KiB PTEs for the state region, eliminating
  any possible size mismatch.

The aggregate-first workaround previously required in `@persistent` var ordering
is **no longer necessary**.

### Performance Budgets

Add `@dense_budget(p99=50us, p50=20us)` to a handler definition to enable
budget enforcement:

* **At compile time**: the compiler rejects generated C++ containing forbidden
  operations (`new`, `malloc`, `delete`, `free`, `syscall`, `ioctl`, `mmap`).
* **At runtime**: the orchestrator tracks p50/p99 per handler over rolling windows
  and emits a structured JSON warning to stderr when a budget is exceeded.

### String Storage

`str` fields cost 264 bytes each in DENSE (256-byte buffer + 4-byte length + 4-byte
padding).  Prefer integers where possible.

### No Closures, No Recursion

Borz handlers are flat and finite — they do not call other handlers, do not
recurse, do not allocate dynamically.  This is what makes single-handler-per-VM
viable on DENSE.

---

## 11. Worked Example — full program

```borz
# bank.borz — minimal bank with account create + transfer.

type Account:
    id:      i64
    balance: i64

msg CreateAccount:
    id:      i64
    opening: i64

msg Transfer:
    from_id: i64
    to_id:   i64
    amount:  i64

trait Ledger:
    fn total(self) -> i64

actor Bank:
    @persistent var a1: i64 = 0
    @persistent var a2: i64 = 0
    @persistent var count: i64 = 0

    @http(method="POST", path="/create")
    @cli(command="create", desc="Open an account")
    on msg CreateAccount:
        if count >= 2:
            fail(code=429, msg="account limit reached")
        count = count + 1
        if count == 1:
            a1 = msg.opening
        if count == 2:
            a2 = msg.opening
        response(id=count, balance=msg.opening)

    @http(method="POST", path="/transfer")
    @cli(command="transfer", desc="Move money")
    on msg Transfer:
        if msg.from_id == 1:
            if a1 < msg.amount:
                fail(code=402, msg="insufficient funds")
            a1 = a1 - msg.amount
            a2 = a2 + msg.amount
        if msg.from_id == 2:
            if a2 < msg.amount:
                fail(code=402, msg="insufficient funds")
            a2 = a2 - msg.amount
            a1 = a1 + msg.amount
        response(a1=a1, a2=a2)

impl Ledger for Bank:
    fn total(self) -> i64:
        ret a1 + a2
```

Compile and run:

```bash
borz compile bank.borz --target native
./bank.native create   --field id=1 --field opening=100
./bank.native transfer --field from_id=1 --field to_id=2 --field amount=30
```

---

## 12. Grammar Summary (EBNF, simplified)

```ebnf
Program       ::= TopDecl*
TopDecl       ::= Import | Use | Msg | Enum | Type | Trait | Impl | Actor | Fn
Vis           ::= "pub" | "priv"                    # optional; default = pub

Import        ::= "import" String ("as" Identifier | "as" "*"
                                  | "import" "{" SymList "}")?
Use           ::= "use" "go" ("_" String | RawBlock | String ("as" Identifier)?)
Msg           ::= Vis? Decorator* "msg" Identifier ":" NEWLINE INDENT Field* DEDENT
Enum          ::= Vis? "enum" Identifier (TypeParams)? ":" NEWLINE INDENT Variant+ DEDENT
Variant       ::= Identifier ("(" Field ("," Field)* ")")?
Type          ::= Vis? Decorator* "type" Identifier (TypeParams)? (":"
                  NEWLINE INDENT Field* DEDENT
                  | "=" TypeExpr)
Field         ::= Identifier ":" TypeExpr NEWLINE
TypeParams    ::= "[" Identifier ("," Identifier)* "]"

Trait         ::= Vis? "trait" Identifier ":" NEWLINE INDENT FnSig+ DEDENT
FnSig         ::= "fn" Identifier "(" Params ")" ("->" TypeExpr)? NEWLINE
Impl          ::= "impl" Identifier "for" Identifier ":" NEWLINE INDENT FnDef+ DEDENT
FnDef         ::= "fn" Identifier "(" Params ")" ("->" TypeExpr)? ":" Block
Fn            ::= Vis? "fn" Identifier "(" Params ")" ("->" TypeExpr)? ":" Block

Actor         ::= Vis? Decorator* "actor" Identifier ":" NEWLINE INDENT ActorBody DEDENT
ActorBody     ::= (VarDecl | LifeCycle | Handler | FailHandler | Decorator)*
VarDecl       ::= Decorator* ("var"|"let"|"val") Identifier ":" TypeExpr ("=" Expr)?
LifeCycle     ::= ("on_start" | "on_recover" | "on_migrate") ":" Block
Handler       ::= Decorator* "on" "msg" Identifier ":" Block
FailHandler   ::= "on" "fail" Identifier ":" Block

Block         ::= NEWLINE INDENT Statement+ DEDENT
Statement     ::= Assign | LetVar | If | Match | For | Send | Response
                | Fail | Log | Ret | Every | After | Within | Infer
                | CallGo | CallCpp | Expr
CallGo        ::= "call" "go" RawBlock              # inline Go body
CallCpp       ::= "call" "cpp" RawBlock             # DENSE only

Send          ::= Identifier "<-" (Identifier | TypeCall)
TypeCall      ::= Identifier "(" ArgList? ")"
ArgList       ::= Arg ("," Arg)*
Arg           ::= (Identifier "=")? Expr           # named or positional

Decorator     ::= "@" Identifier ("(" KVList? ")")?
TypeExpr      ::= PrimType
                | Identifier ("[" TypeList "]")?    # e.g. List[i64], Map[str, i64]
PrimType      ::= "i8" | "i16" | "i32" | "i64"
                | "u8" | "u16" | "u32" | "u64"
                | "f64" | "bool" | "str"
                | "bytes" | "blob"                  # RFC-BORZ-EXT-001
                | "bytes" "(" Int ")"               # fixed-width binary
```

---

## 13. Tooling

| Command | Purpose |
|---------|---------|
| `borz compile <file.borz> --target <t>` | Compile to chosen target. |
| `borz run <file.borz> <Actor> <Handler> [--field k=v ...]` | Drive a DENSE binary with state persistence. |
| `borz run <file.borz> <command>` | Run a `@cli`-annotated handler by command name. |
| `borz-httpd <file.borz> --port 8080` | Serve `@http` handlers as a development HTTP server. |
| `borz test <file.borz>` | Run scenarios from co-located `scenarios.yaml`. |
| `borz bench <file.borz>` | Run benchmarks (DENSE + native head-to-head). |

---

## 14. Supervision Tree & Fault Strategies (RFC-0003)

Borz implements Erlang/OTP-style supervision for production fault-tolerance.

### 14.1 The `@supervisor` decorator

Any actor annotated with `@supervisor` becomes a supervisor: it declares
and manages the lifecycle of one or more child actors.

```borz
@supervisor
actor Manager:
    @persistent var alive: i64 = 0

    supervise:
        strategy: one_for_one
        max_restarts: 5
        within: 10s

        child Worker
        child Logger as loggers[2]   # two Logger instances (fan-out)

    on msg ChildFailed:
        log("child down: " + msg.actor)
    on msg Status:
        response(alive=alive)
```

### 14.2 The `supervise:` block

The `supervise:` block is placed at the top of a `@supervisor` actor body,
after `@persistent var` declarations and before handlers.

Fields:

| Field | Values | Description |
|-------|--------|-------------|
| `strategy` | `one_for_one` (default), `one_for_all`, `rest_for_one` | Restart strategy |
| `max_restarts` | integer | Max restarts within `within`. 0 = unlimited. |
| `within` | `5s`, `1m`, `30s`, … | Sliding restart window. |
| `state_policy` | `preserve` (default), `reset` | Whether to keep state across restart. |

Child declarations:

```
child ActorName                        # single instance
child ActorName as alias[N]            # N instances (fan-out sugar)
```

### 14.3 Restart strategies

- **`one_for_one`** — Only the failed child restarts. Other children are unaffected.
- **`one_for_all`** — When any child fails, all children are cancelled and restarted.
- **`rest_for_one`** — The failed child and all children declared *after* it restart.

### 14.4 Restart quotas and escalation

`max_restarts` and `within` form a **sliding-window quota**. If more than
`max_restarts` restarts occur within the `within` window:

1. The supervisor **escalates**: emits a `ChildFailed` system message to the
   parent supervisor (if any).
2. If there is no parent, the runtime exits non-zero.

### 14.5 The `on_recover:` lifecycle hook

`on_recover:` runs after a supervised restart, once, before the actor
resumes normal message processing. It receives an implicit `msg`:

```borz
on_recover:
    log("attempt " + i64_to_str(msg.attempts))
    if msg.reason == "guest_fault":
        retry_count = retry_count + 1
```

Standard recovery message fields:

| Field | Type | Description |
|-------|------|-------------|
| `msg.reason` | `str` | Why the restart occurred (`"guest_fault"`, etc.). |
| `msg.attempts` | `i64` | Number of times this actor has been restarted (1-based). |
| `msg.previous_state_version` | `i64` | State snapshot version before restart. |
| `msg.restored_from_snapshot` | `bool` | Whether a snapshot was loaded. |

### 14.6 Grammar additions

```ebnf
Actor         ::= Decorator* "actor" Identifier ":" NEWLINE INDENT ActorBody DEDENT
ActorBody     ::= (VarDecl | SuperviseBlock | LifeCycle | Handler | FailHandler | Decorator)*
SuperviseBlock ::= "supervise" ":" NEWLINE INDENT SuperviseItem+ DEDENT
SuperviseItem  ::= StrategyItem | MaxRestartsItem | WithinItem | StatePolicyItem | ChildItem
StrategyItem   ::= "strategy" ":" Identifier
MaxRestartsItem ::= "max_restarts" ":" Integer
WithinItem     ::= "within" ":" DurationLiteral
StatePolicyItem ::= "state_policy" ":" Identifier
ChildItem      ::= "child" Identifier ("as" Identifier "[" Integer "]")?
DurationLiteral ::= Integer ("ms" | "s" | "m" | "h")
```

### 14.7 Examples

- `examples/standard/50_supervision_basic/` — `one_for_one` strategy.
- `examples/standard/51_supervision_oneforall/` — `one_for_all` with state reset.
- `examples/standard/52_supervision_escalate/` — quota exceeded; `ChildFailed` handler.

---

## 15. Versioning & Stability

Borz follows semver at the **language** level. The IR and binary formats are
expected to break between minor versions until `1.0`. Every example in
`examples/` is part of the test corpus and compiles on every commit; treat
them as executable specification.

Track ongoing work in `documents/docs/project-status-*.md`.

---

## 15. Modules & Packages

Implemented by RFC-0001. A **module** is a directory of `.borz` files with an
optional `borz.toml` manifest at its root.

### 15.1 Manifest (`borz.toml`)

```toml
name        = "vote"          # required: [a-z][a-z0-9_-]*
version     = "0.3.0"         # required: semver X.Y.Z
description = "..."           # required: ≤ 280 chars
license     = "MIT"           # required: SPDX identifier
authors     = ["Alice"]       # optional
homepage    = "https://..."   # optional
repository  = "https://..."   # optional

[dependencies]
common = { path = "../common" }   # local path dep
telemetry = "^1.0.0"             # registry dep (semver range)

[backends]
targets = ["native", "demix", "dense"]
```

Validate with `borz pkg vet`. Scaffold with `borz pkg init`.

### 15.2 Import forms

```borz
# Aliased import — exposes symbols under a namespace prefix.
import "./shared/types.borz" as Types
# Use:   on msg Types__PublicEvent:    (internal prefix: Alias__Symbol)

# Wildcard import — only allowed for std/ modules.
import "std/option" as *
# Use:   on msg Some:   (no prefix)

# No-alias import — backward-compatible; symbols kept as-is.
import "./types.borz"
```

**Rule:** `as *` is only allowed for paths starting with `std/`.

### 15.3 Visibility (`priv`)

By default every top-level declaration is module-public. Mark it `priv` to
restrict it to the current file only:

```borz
priv type sessionKey:
    epoch: i64
    nonce: i64

priv msg InternalPing:
    dummy: i64
```

`priv` applies to: `actor`, `msg`, `type`, `enum`, `trait`.

### 15.4 Module fingerprints

After resolving imports, the compiler computes a **SHA-256 fingerprint** over:

1. Sorted message/struct/enum definitions (canonical byte form).
2. Sorted dependency fingerprints.
3. Compiler version string.

The fingerprint is used as the `ActorStateVersion` for all actors in the
module, ensuring state snapshots are rejected when a schema changes.

### 15.5 `borz pkg` commands

| Command | Effect |
|---------|--------|
| `borz pkg init` | Scaffold `borz.toml` |
| `borz pkg vet` | Validate `borz.toml` |
| `borz pkg add foo@^1.0` | Add/update a dependency |
| `borz pkg fetch` | Resolve deps, write `borz.lock` |
| `borz pkg tree` | Print resolved dep graph |
| `borz pkg search <term>` | Search the registry |
| `borz pkg outdated` | List deps with newer versions |
| `borz pkg publish` | Publish to the registry |

### 15.6 Standard library (`std/`)

Bundled with the compiler via Go's `embed` package. Available modules:

| Path | Contents |
|------|----------|
| `std/option` | `Some` / `None` (full sum type via RFC-0002) |
| `std/result` | `Ok` / `Err` plus `?` operator (RFC-0090) |
| `std/str`, `std/string` | String operations and split / join / contains |
| `std/time` | Wall clock + RFC-BORZ-EXT-005 three-source model: `wall_now_ms()`, `actor_clock_ms()`, `ledger_now()`, `duration_ms(a, b)` |
| `std/math` | `min`, `max`, `abs`, `pow`, transcendentals |
| `std/random` | Seedable PRNG; `randint`, `randf`, `random_bytes` |
| `std/crypto` | SHA-256, HMAC, AES-GCM, RSA-OAEP, ed25519 (FFI through `use go`). `ed25519_verify_bytes(pub, msg, sig) -> bool` has a `call cpp` peer that links libsodium on DENSE (RFC-BORZ-EXT-010). |
| `std/blake3` | BLAKE3-256 over `bytes`. `hash256_hex`, `hash256`, `hash256_verify`, `hash256_keyed_hex`. Dual `call go` (zeebo/blake3) + `call cpp` (libblake3.a) for cross-target byte-identical output (RFC-BORZ-EXT-010). |
| `std/sqlite` | Embedded SQLite (`use go _ "modernc.org/sqlite"`) |
| `std/http`, `std/fetch` | Outbound HTTP client |
| `std/semver` | Version range parsing / comparison |
| `std/conv` | Lossy + strict numeric / string conversions |
| `std/dom` | Browser-WASM DOM helpers (`@dom`-decorated handlers) |
| **`std/bytes`** | RFC-BORZ-EXT-001. Variable-length binary helpers: `bytes_empty`, `bytes_of`, `bytes_concat2/3`, `bytes_from_hex`, `bytes_from_b64`, `bytes_from_str`, `bytes_repeat`, `bytes_with_capacity`, `len_b`, `at_b`, `slice_b`, `eq_b`, `cmp_b`, `index_b`, `to_hex_b`, `to_b64_b`, `to_str_b`, `put_u8`, `put_u16/32/64_le`, `get_u16/32/64_le`, `next_chunk`. |
| **`std/collections`** | RFC-BORZ-EXT-002. Deterministic sorted-key views: `map_keys_sorted_str`, `map_keys_sorted_u64`, `map_keys_sorted_i64`, `set_sorted_str`, `set_sorted_u64`, `bytes_lex_less`. |
| **`std/canonical`** | RFC-BORZ-EXT-003. Length-prefixed canonical encoders: `canonical_begin`, `canonical_finish`, `put_bytes_lp`, `put_str_lp`, `read_u32_lp_len`, `read_bytes_lp`, `read_str_lp`, and layout constants `canonical_layout_fixed`, `canonical_layout_length_prefixed`, `canonical_layout_versioned`. |
| **`std/bignum`** | RFC-BORZ-EXT-006. **BigInt** (string-handle, `math/big`-backed): `bigint_zero`, `bigint_from_str`, `bigint_from_i64`, `bigint_to_str`, `bigint_to_hex`, `bigint_add/sub/mul/div/mod`, `bigint_cmp`. **Decimal** (arbitrary precision, four rounding modes): `decimal_from_str`, `decimal_scale`, `decimal_from_units`, `decimal_add/sub/mul/cmp`, `decimal_round(value, scale, mode)` where `mode ∈ {"down","up","half_up","half_even"}`. |

### 15.7 Grammar additions

```ebnf
Import    ::= "import" String ("as" Identifier | "as" "*" | "import" "{" SymList "}")?
PrivDecl  ::= ("priv" | "pub") (MsgDecl | TypeDecl | EnumDecl | TraitDecl | ActorDecl | FnDecl)
BytesType ::= "bytes" | "blob" | "bytes" "(" Int ")"
Canonical ::= "@canonical" ("(" CanonicalArg ("," CanonicalArg)* ")")?
CanonicalArg ::= ("layout" "=" String | "current_version" "=" Int | "endian" "=" String)
```

---

*"By the Agents, Through the Agents, For the Agents." — Borz design motto.*
