

# Target: the finished core

Measure for every step, diff and review in the one-graph release.
Existing code supplies behavior and reusable algorithms, not a
requirement to retain its architecture. Reuse sound algorithms in their
single intended homes. This file owns the current design;
`archive/one-graph-2026-10-31.md` keeps reasons, evidence and history.

Goals, required behavior, acceptance criteria, budgets and explicit
process rules are constraints. Parts, interfaces, algorithms and
ownership describe the current working design. Continuously improve that
design as implementation reveals simpler solutions, updating this
document and affected consumers together. Ask Jeremy only when
proceeding requires changing a constraint, information only he holds, or
a substantive trade-off that cannot be resolved within the agreed
priorities. A departure from the working design alone is not a blocker.

Python APIs are not fixed constraints. If preserving Python semantics
causes non-trivial complexity, stop that piece to discuss simplifying
the API rather than add machinery to the core.

## Goals

- Speed and simplicity (`AGENTS.md`). Workloads: interactive analysis of
  large data, model inference and training, image processing. A
  foundation that works on a GPU with no special cases; this release
  builds the core’s part (part 23).
- Fast code: static kinds; branches and updates by `⍚`, `f@g`, `f@[p]`,
  `x.[p]←`, never dfn guards on traced values. Code written without that
  care: adequate speed. Dynamic kinds: correct, at most 4× the same code
  under static kinds at the same size, except where Jeremy agrees (a
  larger gap where a reasonable rewrite is fast). A guide, “Writing fast
  BPL”, ships with the release: static kinds with explicit sizes on
  literals and data, `⍚`, `f@g`, `f@[p]`, selective assignment into a
  preallocated cache (`K.[p]←x+.×Wk`), index notation, shapes kept the
  same across calls and iterations, `fₐ` where values may leave the real
  domain; and what ordinary code costs.
- Results agree only within the documented accuracy (4 ulps), across
  spellings, routes, sizes and threads.
- “The bar” (below) sets the criteria; it changes only with Jeremy’s OK.
  Done: “Acceptance”.
- `fib` or similar recursive functions do not need to be fast at all
- Value-dependent dynamic `√`, `*`, `⍟` and `○` do not need fast, fused
  or recorded execution. Their explicit approximate forms do. The
  boundary is a required numeric-value read, not a glyph whitelist;
  kinds or proofs can already make a dynamic call fast.
- Ordinary integer arithmetic, including floor `÷`, stays native.
  Unbounded exact arithmetic requires the `ᵣ` domain and need not be
  fast.

## Non-goals (this release)

- Speed for dfn guards on traced values.
- Automatic promotion from ordinary machine integers to unbounded
  integers/rationals; specializing a cached plan on narrower
  opaque-input ranges (part 2).
- Speculated kinds (part 2 reads a kind that depends on values at the
  call instead; `meta/speculation.md` keeps the design and how to add it
  back).
- Identical bits across routes, sizes, threads or spellings; accuracy
  past the documented bounds.
- Reusing a graph across calls from a Python loop or a recursive dfn’s
  calls (decision 5): each call rebuilds.
- Recording through `⍎` or an error guard.
- Symbolic-length expressions in engine types (decision 18): plans
  refill concrete descriptions and maps from run-time counts (part 10).
- Complex and f16 steps in block stages (main’s decision 13); complex
  numbers under `∂` until a gradient convention is chosen.
- The GPU backend, array files, an external backend, bf16 (part 23).
- Range inference through `•index ∞` (decision 11).
- Compact-loop AD, native loop lowering and checkpointing; native
  segmented/ragged storage and execution, including continuous batching.
  Existing Power, dfn fold/scan and nested-array behavior remains in
  scope (parts 1, 12, 13, 15).

## Measures

- 1 mechanism per job; 1 home per fact. A 2nd route for a job is a gap,
  faster or not: speed = a rule or a scheduler choice over nodes.
- Language rules: the primitive’s row (`Primitive::info()`). Math facts:
  its rustymath object. Nothing else keys on a primitive.
- No decision by spelling (name checks, walks of a function’s form),
  except where meaning comes from form: inverses, `discards`, fill
  options, display.
- A new kind of thing = data in a table, not arms across match sites. A
  variant that needs arms in many matches → the enum lacks a shared
  shape: fix the enum.
- Rust closures never stand in for BPL functions or bodies. A body is a
  function value or a recording.
- No cache beside the program cache (recordings) and the JIT’s code
  table (compiled code), each its fact’s 1 home; no 2nd copy of items.
- A recording, or a forced graph’s cached plan, assumes only its key and
  facts derived from kinds, keyed literals, graph operations and current
  shapes/counts (part 2). All captured free data are call-time inputs,
  never separately reread or compared facts (part 10).
- Errors: construction, shape, type, name and host-I/O errors remain
  checked in both modes. Graph-numeric data errors are checked under
  dynamic kinds; static graph-numeric execution does not record them.
  Host-kind arithmetic remains checked in its normal Call stage. Invalid
  static numeric indexed reads use the fill, including from empty
  arrays; invalid static writes skip those positions. Other invalid
  static numeric results are unspecified. Every kernel stays within its
  operands’ bounds and its declared result range, with fallback values
  included by the existing range rules. Valid results, exact results and
  documented finite accuracy are unchanged. The mode comes from each
  call’s origin, never the session at force time. Rustymath owns the
  error slot held by basedpl’s session. Checked kernels and executors on
  any thread record the first error and increment its count; each runner
  leaves results downstream of an error pending. Computation-only
  execution (`program::execute`) records without raising. Actual host
  materialization/readback (`Node::into_storage`) is a checkpoint and
  raises before exposing a failed value. Other checkpoints are effects,
  error guards’ exits, display and statement ends. Leaving an error
  guard forces the work built inside it. When and how an error surfaces
  isn’t promised.
- 1 mode (tinygrad): every numerical operation builds a node and waits;
  semantic language forms expand to such calls; a read forces what it
  needs and runs its graph’s plan from the 1 cache, scheduling only on a
  miss. Each stage has a budget (“Cost model”); a stage over budget → a
  cheaper stage, never a 2nd route.
- basedpl and rustymath never hold 2 machineries for 1 task.
- Representation changes happen at explicit boundaries. Repeated
  translation or reconstruction in consumers is a reason to redesign the
  representation or boundary, not add adapters.
- No per-position side data on operand lists: a fact about an operand
  travels in its value (a literal node) or is derived when needed by 1
  function from the operation and its operands (`CallLayout`,
  `Operation::roles`), never stored and carried through rebuilds.
- Measure source growth and deletion primarily under `src/` in each
  repository. Report tests, docs and Python support separately.
- Substantive complexity needs a demonstrated current need. Remove
  deferred capabilities’ protocols and consumer branches rather than
  retain them for later. Reuse the last tagged releases’ sound
  algorithms under one current mechanism; `meta/foundation-reset.md`
  maps removals and reuse from the over-complex checkpoints.
- If no clean design can meet the fixed constraints without Jeremy’s
  decision, pause the goal and ask under the authority rule above. Do
  not preserve or reintroduce complexity, add a compatibility route, or
  silently weaken a budget to continue.

## The build, in order

Mechanism owners write connected changes in parallel. One integration
coordinator builds and tests frozen snapshots while source editing
continues. Main changes assignments, boundaries and build cadence as the
work requires. These operating choices do not change the goals or
acceptance gates.

1.  **Declare the interfaces needed next.** Put shared declarations in
    their target homes before dependent edits. The mechanism owner
    iterates the design and publishes meaningful contract changes to
    consumers and main. Repeated caller changes indicate a
    responsibility that needs a shared home.
2.  **Plan a coherent chunk.** Keep one mechanism table with the
    violated contract, owner, planned duplicate removal, status and next
    proof. Link detailed investigations on demand. Diagnose several
    examples at their common source, using the working tagged release
    where useful. Source-file boundaries do not determine ownership.
3.  **Write the end state.** Delete duplicate machinery as its
    replacement is written. Keep tightly connected work with one owner.
    Other owners continue independent work without waiting for a build,
    another note or routine approval. Do not add an adapter, cache or
    temporary route to cross an unfinished boundary. 3a. At the
    source-complete milestone, use ghapi in the kernel to commit all
    intended files in both repositories before compile integration.
    Choose the messages without asking for approval.
4.  **Integrate coherent snapshots.** The coordinator owns builds,
    mechanical compiler fixes and consolidated checks in the paired
    build worktrees. Every change to a shared fact’s owner gets its own
    coherent snapshot, including necessary caller migrations and
    deletions. Run focused contrasting cases, then the full sweep before
    dependent work builds on that contract. Do not bundle unrelated
    fixes because one writer produced them. Treat compiler errors as
    feedback on the design; simplify a shared representation rather than
    patch many callers. Return substantive corrections to the mechanism
    owner. Writers need no global freeze or file lease. 4a. At the
    completed integration milestone, use ghapi in the kernel to commit
    all intended files in both repositories, choosing the messages
    without asking for approval.
5.  **Correctness and performance checks.** Keep the two-second per-case
    checks enabled throughout the work. Explicitly identified full-size
    cases over deadline go in `tests/skipped.txt`, with their reasons
    and re-enable criteria; report them as unresolved skips. Keep value
    failures, route failures, unfinished checks and budget results
    separate. Never shrink a case or relax a budget to hide a failure.
    Performance improvements use cheaper shared implementations, never
    second routes.

### Evidence and handoffs

The coordinator’s automated helper inventories applicable cases and
modes, reuses healthy workers and continues after watchdog stops. A mode
that did not run is unknown; replay with a failed warm-up is blocked. A
first observed failure is a regression only if the same case and mode
previously passed. Every result names its source pair, immutable build,
fixtures and profile. A focused check does not prove full acceptance.

Main samples source during writing and arranges independent reviews of
substantive chunks while other work continues. Reviews name the fact’s
owner, who can construct it, the duplicate decisions and entry points
removed, and any new caller obligations. A passing case alone does not
close a duplicated contract. Mechanism owners publish coherent handoffs
when a boundary or capacity limit prevents useful progress. There is no
second approval ceremony for routine API names or local refactorings.

Flag missing tools, configuration gaps and misleading telemetry
immediately. Track each gap in the integration ledger until an
invocation of the actual helper verifies the fix. Never substitute an
empty report or another tool’s result as proof that it works.

### Keeping call sites from spreading again

- Lengths, errors, levels and settings each have one home. Engine code
  sees concrete lengths, graph-numeric errors use the session slot
  rather than `Result`s threaded through kernels (construction, shape,
  name and host errors remain fallible), and origins keep the settings
  they read.
- Other owners consume declared interfaces. Publish meaningful changes
  to their consumers and main before dependent edits. If a change
  repeatedly crosses owners or spreads a concern through callers, move
  the connected work together or fix its home; do not add glue.
- Before changing a boundary, classify affected sites: callers that
  already read and retain the result; item-processing helpers whose
  parameters narrow; unrelated code that would only pass a new proof
  along. Stop and redesign the third kind. Prefer existing storage views
  to a new wrapper. At milestones, report caller spread, net source
  lines and any work outside the previous ownership boundary.

### Existing code: reuse algorithms, remove duplicate mechanisms

The last tagged releases supply working behavior and reusable
algorithms. Keep one architecture and reuse sound graph, iteration,
traversal and numerical algorithms within it. A function is not obsolete
merely because it predates this target. Delete obsolete machinery as
replacements are written. Do not require retained-call inventories or
separate source-completeness/deletion audits. Main samples design
quality during writing; compile integration finds wiring gaps. Existing
callers, privacy or names do not justify a second route, cache or
compatibility layer.

Preserve released numerical behavior and existing test expectations
except for explicitly agreed changes. Checked-error handling changes how
required errors are reported, not which inputs are errors; a defined NaN
result stays NaN. Compare regressions with the released implementation
and reuse its rules before adding special cases. Fix straightforward
regressions without changing expectations. If preserving behavior needs
substantial complexity, discuss the concrete semantic alternative and
its simplicity or demonstrated speed benefit before changing code or
tests. Outside the authorized performance phase, additional performance
comparisons require Jeremy’s permission.

## Flow

call with declared roles → parameter observation → semantic forms (part
5) → levels (`Level::rule`: newest level’s rule) → single row factory →
pending node. The origin keeps what replay and refill need (part 4). A
read walks live nodes and inputs (`Live::of`), looks up its structure
key in the 1 cache (part 10), checks key and legality guards, and
schedules only on a miss: `Program::of` → rewrite → order → stages →
plan. A hit refills changed lengths or count parameters. Run preparation
makes cheap choices from actual counts, then executes each stage or
compiles its selected version when that pays (parts 8–10). Checked
errors go to the session’s slot and are raised at checkpoints. Outside a
recording an effect runs at its call, in order among effects, forcing
what it reads. A recording retains the ordinary graph as a data-free
body template. Cached calls bind current inputs and instantiate pending
nodes through the shared row factory and node construction, without
scheduling native execution or repeating full primitive dispatch. A read
owns execution planning and its cache.

## Role models

- tinygrad: 1 lazy graph of interned nodes (`UOp`); every call builds
  and waits, only a read computes (`realize`); running a graph separate
  from reading its result (`realize()`, `numpy()`); a rule matcher
  indexed by root op with early rejection (`PatternMatcher`); stage ends
  by its indexing rule (`schedule/indexing.py`); compiled kernels cached
  by structure; recordings from real runs replayed (TinyJit), effects as
  ordered nodes where TinyJit runs them only while capturing.
- JAX: one primitive entry and newest-level rules; primitive JVPs,
  linearization/transposition, batching and Select input gating; ordered
  effects and explicit random state. BPL reuses these transformation
  ideas with call-time data snapshots. A Python loop traced by JAX is
  unrolled; `lax.scan` explicitly requests a compact loop. The
  foundation follows the unrolled approach for actual Power and dfn
  fold/scan calls. Compact-loop AD and checkpointing are later measured
  improvements, not prerequisites for one graph. JAX’s abstract tracers
  and eager execution are not copied. BPL’s existing length-class/refill
  design keeps concrete run-time counts; arbitrary cached replay under
  older levels is reviewed separately from correct nested
  transformations.
- Also: Futhark’s bulk map/reduce/scan differentiation, fusion and
  actual-size choices; Dex’s preservation of whole-array work and
  indexed accumulation; XLA’s rewrites after transforms and cheap
  recomputation; DuckDB’s conservative range facts; Dynamo’s retrace
  limit. Dex/Futhark structured-loop AD and DuckDB segmented storage
  supply deferred ideas to measure later. Tinygrad commonly builds an
  unrolled graph from host-language iteration. These systems are role
  models for mechanisms we need, not a catalogue of capabilities that
  the foundation must combine.
- Not taken: tinygrad’s gradient after the fact (a read before the loss
  would cut it); JAX’s abstract tracers (BPL traces real runs, decision
  6.4) and its eager evaluation (BPL is lazy throughout).

## Parts

Each: job; callers; not (what must not exist).
`meta/foundation-reset.md`’s “Deletion checklist” lists the named old
code still in the tree.

1.  Values (`array.rs`, `array/storage.rs`). `Value`: atom | stored
    array (`ArrayData`: compact, on rustymath buffers and views, or
    mixed) | pending (`Value::Pending`) | carried (a derivative or batch
    level’s value, `Value::Carried`). Mixed arrays hold nested values,
    records and functions; a nested item may be pending (a pytree
    holding a tracer); numeric leaves enter the graph. Uniform results
    assemble into compact arrays with more axes; ragged ones stay
    nested; native segmented storage (entries/shapes/leaf/prototype) is
    deferred. Native kinds: Boolean, fixed and ordinary integers,
    f16/f32/f64, complex, characters. `ᵣ` values, mixed kinds, functions
    and records are host values: a host boundary borrows the actual
    value’s nonnumeric structure, never inventing a numeric description.
    Mixed-array traversal, copying, prototypes, comparison, display and
    Python/CSV/JSON conversion stay. Every host read of a pending node’s
    items passes `Node::into_storage` (part 10’s chokepoint): it refuses
    unbound linear reads and judges others by input roles;
    `ArrayData::storage` reads stored arrays, not trace inputs. A
    partial read is a `Copy` node; the scheduler computes only what it
    reads (tinygrad’s shrink pushed into a kernel). Leaf buffers are
    host-owned; external and GPU memory: part 23. Not: segmented storage
    or five-part trees; packing per producer; items cached twice; host
    reads past the chokepoint; carriers in stored data; level code in
    storage.
2.  Descriptions (`program::Desc`; JAX abstract eval): native kind, atom
    flag, layout, range; nonnumeric structure stays in Trees or the host
    value. The row factory derives a step’s description from its origin
    in 1 kind/shape/range pass over operand descriptions and parameter
    values, before choosing the step; `Desc::leaf` describes a leaf
    once, on entry. Kinds: Boolean; fixed integer of a width; ordinary
    machine integer of a width; float of 16, 32 or 64 bits; complex of
    either width; character; weak or strong. Kinds and their join: 1
    home, `number::Kind` and `number::Kind::join`; rustymath’s canonical
    machine-dtype promotion stays shared; `array/storage.rs`’s 1
    stored-result joining policy serves assembly and descriptions
    through that join. Integral weak+fixed joins use the common dtype,
    retaining the fixed domain. Only an existing keyed ordinary weak
    literal contributes its minimum representable integer dtype through
    `Kind::literal`; opaque inputs retain their declared kind bounds.
    Computation and result consume the same projected kinds. Literal
    projection in containers applies only to a fixed-domain result;
    ordinary static strands retain their ordinary width. No value-fit
    admission or new provenance flag. Static kinds join results computed
    apart (writes, compacting selects, map assembly, `⍣` histories) to 1
    compact kind, as JAX gives `vmap`, `where` and `scan` outputs 1
    dtype; strands, rows and dynamic kinds keep each item’s kind. Wider
    accumulators come from seeds, conversions and the ordinary-integer
    width policy, never an accumulator field. Ordinary integers use
    machine arithmetic: dynamic kinds narrow where intrinsic kinds and
    in-graph ranges prove a width suffices, else use i64 and wrap, never
    inspecting opaque input extrema; static ordinary and explicit fixed
    integers wrap at their width. Integer `÷` floors, as `÷ₓ` does;
    float arguments keep float division and mixed promotion; `÷ₐ`, `÷ᵣ`
    and the unbounded `ᵣ` domain: part 21. Dynamic kinds keep
    value-dependent kinds (`√ * ⍟ ○` on reals can give complex): where
    ranges don’t decide one, the call reads the operands it depends on
    (part 5’s roles), inside and outside traces (part 10). Part 21’s
    primitive real forms and `÷ₓ` (floor division to a machine integer)
    resolve before the unmarked kind rule and reads, so never depend on
    values. Under dynamic kinds, real-float `⌊ ⌈` give ordinary integers
    saturating at the 64-bit range: positive infinity gives the maximum,
    negative infinity the minimum, and NaN gives 0. Static unmarked
    real-float `⌊ ⌈` retain floats; explicit `⌊ₓ ⌈ₓ` create machine
    integers at the selected width in both modes. Exact-domain and
    Gaussian complex rounding keep their domains. Monadic `×` of floats
    gives an ordinary integer under dynamic kinds. Keyed and named
    layouts are graph values: keyed agreement is a node whose maps and
    length come from the keys at run time, where a key missing on one
    side reads as the fill (selecting a missing key: INDEX error, part
    20); a cache key compares key sets by identity before contents; a
    layout’s renderer travels in descriptions. Cacheable renderers are
    canonical data-free callables with no run-time bindings (stateless
    native ones, the image renderer included); one needing data or
    bindings renders normally but declines recording and graph caching,
    through the same scheduler. Ranges (DuckDB statistics) come from
    each object’s interval rule over operand ranges. Opaque inputs
    (forced graphs, recordings, recorded bodies) have intrinsic kind
    bounds; narrower carried ranges are dropped, even proved ones.
    Literals keep their values in the key; in-graph operation guarantees
    and shape/count bounds at actual lengths stay, through unrolled
    iteration too. Ranges keep `√` and `⍟` real without reading items,
    choose narrower machine work, drop a gather’s bounds check where its
    indices lie inside the axis, and prove an object’s repair predicate
    false (part 6) through that predicate, never another threshold or
    admission policy. Possibly wrapped integer results get conservative
    ranges, never the unbounded interval: the minimum signed integer’s
    magnitude may wrap negative, so it can’t prove nonnegativity. A
    read’s kind and range cover every numbered source it may select, its
    fill included (an invalid static read’s fallback where validity
    isn’t proved), from descriptions alone. A fold’s range uses its
    actual item count or a conservative bound for the plan’s accepted
    lengths. Refill re-derives and validates count-dependent facts (part
    10). Not: a segmented description; opaque-input range boxes or
    numeric contracts on opaque input values; tracking assumed narrow
    input ranges; range-failure/retrace counters or invalidation on
    changed input extrema; automatic ordinary-integer overflow to BigInt
    or integer division to rational; a description from a value’s
    `layout()` in a trace; parallel description helpers; a 2nd shape
    rule.
3.  Graph (`graph.rs`; tinygrad `UOp`). `graph::Node`: a step and its
    origin’s operand list, each read through a rustymath `Mapping`, with
    a description per native output and its logical output tree; or a
    leaf: an input (key position, role and a free value’s name; no step)
    or a computed buffer. Nodes, leaves included, are interned by part
    4’s structural origin facts (`keeping`). Forced → `result`: a read
    replaces the node with its result and drops its step and operands
    (tinygrad’s `realize`), so later graphs read a leaf and no rule
    matches across it. `Live::of` walks a read’s live nodes, hashes its
    key and gathers its inputs; `Program::of` converts them only on a
    cache miss; the scheduler, rules, recordings and `verify` read only
    `Program`. A view is a `Copy` node composed into its readers’ maps,
    stored (1 copy) only for a reader whose kernel can’t read through a
    map, as 1 capability test (`reads_through`) decides; readers of a
    shared pending copy compose their own reads without storing it.
    Rustymath’s single numbered-source read planner composes copy chains
    unmaterialized: `Mapping::composed` for single-source affine maps,
    `Steps::through` for composition by inner axes beyond them; fill
    routes stay reads of scalar sources (part 4). Not: node builders
    outside dispatch; scheduling the live graph directly.
4.  Steps (`program::Step`): the IR.
    `Map Convert Copy Gather Fold Product At Picks Join Call Draw Effect Index`
    (`Index`: an affine index step), each with its normal runner and
    glyph; the row factory derives a step’s description and reads at
    construction and on refill, rule outputs and lowered steps included.
    Operands are the origin’s list, named by position; no variant holds
    another list. The compiler’s constant producer marks true source
    constants as literal nodes (`Value::constant`), including constant
    arrays, strings and records; functions keep their binding category.
    Names, captured data and formal arguments are registered inputs.
    Containers hold their items unread, including literal nodes. Host
    code forwarding an operand forwards that held value rather than
    reading and reconstructing it. An actual host item read is an
    observation boundary and returns a stored value. Data-independent
    constants computed by host code enter through the same literal
    constructor. A shape-derived value retains its count/refill
    dependency or structural observation guard; a current length is
    never frozen merely because it came from a description. The origin
    keeps: the operation; function values; operands as reads in call
    order; all captured free data and operator data, snapshotted and
    flattened at the call with `Trees` (part 10); roles; mode (static or
    dynamic kinds, Mathish: bits, not a parameter read); argument trees
    for flattened calls; the call’s source site, which internal calls
    preserve and cached execution binds anew. Host operations key the
    source base that relative paths need, never diagnostic ranges or
    source text; numeric keys omit sites. Numeric values a kernel reads,
    such as tolerance, are operands with parameter roles named by the
    row’s settings; host Power convergence reads its captured numeric
    scope, with no Loop setting or data protocol. Callable origins
    capture the numeric defaults that interpret their deferred bodies; a
    scoped activation uses those setting inputs and the origin’s mode
    and never changes process defaults, which only an explicit
    preferences effect updates, with active contexts, through the same
    settings home. Pending nodes keep their call’s settings. A route is
    a step, never a builder; a new job takes an existing step where one
    serves. Batching and JVP rules belong to rows; host iteration uses
    ordinary transformed body calls, not a loop rule; transposes are
    part 15’s pass, keyed on linear facts. Index steps: `⍳` and `•index`
    are affine generators, never stored; `+ -`, negation and `×` by a
    constant keep them affine. Selection plans strides, wrapped and
    padded reads from coefficients (`Stride`, `Rotated`, `Padded`);
    clamped and reflected reads are `Padded` maps with `•edge` modes,
    not gathers; `Table::affine` stays for index arrays that are data. A
    chosen numerical fill is scalar data, described by kind and range
    without reading it; an options record gives its structure as
    parameters and its fill field as a data input; settings, counts,
    ranks and tolerances stay parameters. `Copy` (base and optional
    fill) and `Join` take numbered sources through rustymath’s shared
    `ReadPlan` (part 9). `Join` finds positions by running the moving
    primitive on ids and serves strands, `,`, `⍪`, compaction, Under
    writes and joins’ transposes; read-back distributes cotangents to
    every source through part 15’s one mechanism. `Call` runs a planned
    primitive outside block stages (sort, grade, search, `⌹`) on forced
    operands, in its own stage. `Effect` reads the preceding effect’s
    token and its own operands; a stage ends at it.
5.  Dispatch (`Operation::dispatch(Origin)`; JAX `Primitive.bind`,
    tinygrad’s lazy graph): 1 numerical entry for primitives,
    folds/products, selects, writes, notation/joins, conversions, draws
    and effects; engine consumers call it, never private builders. In
    order: semantic forms resolve (row-defined forms and marks call
    their constituent operations through dispatch; host iteration
    expands actual function calls, part 13; Select normalizes into
    ordinary calls and reads, part 14); callable, data and
    description-only uses are classified before callable captures are
    traversed (containing a function isn’t calling it); captures of
    executable callables and operator data join the ordered call-time
    inputs; then the newest level’s rule handles the call through the
    same entry. `Operation::roles` names data and call-time parameter,
    Count, kind and structural reads before they occur. Higher-order
    consumers use part 19’s shared typed-body query before selecting
    native Fold, Product or marked forms. Already-declared primitive
    operations retain the constant-time base case. 1 borrowed CallView
    lends the origin, bound functions, resolved role values and
    descriptions. 1 row factory computes kind, shape, range and geometry
    once, chooses the Step (or the normal host Call) and derives its
    reads from those results; that combined result serves live
    construction and refill alike; `fusion::built` constructs the
    pending node. A host-kind operation uses the normal Call stage;
    unknown result structure is an explicit host/recording boundary,
    never a generic pending-header protocol. A partial pending read is
    an ordinary Copy; a fold to an atom stays pending. Not: a second
    eager/traced numerical route; per-call force policies; builders or
    executors in consumers; speed rewrites at dispatch; metadata
    references captured as callees; special pending-atom readers instead
    of roles; independent describe/plan/maps callbacks recomputing the
    same geometry; an extra planner cache.
6.  Rows (`primitive/facts.rs`) and rustymath objects (`facts.rs`,
    `recipe.rs`, `structure.rs`, `derivative.rs`). Row: its object; plan
    (kind and shape rules over known lengths); batching (`cells`:
    operation, wholes and frame count in, a dispatch call out; semantic
    host iteration uses the same body-call rules); inverse; Under form
    (structural, or computational through the inverse); linear facts
    (linear operands, jointly or each, and zero-tangent calls: JAX’s
    `deflinear`, `defbilinear`); identity (its object’s `identity` where
    one exists; structural functions such as `,` hold their own); cost;
    row facts (`passes`, `alternates`, `first_axis`); structural
    validity. No transpose per row (part 15). Object, each fact once:
    common formula; optional, separately named repair predicate and
    repair routine; partials; ranges; numeric validity conditions; fold
    properties. The repair predicate is an Ops formula giving 1 Boolean
    per item, shared by native and JIT execution; it owns
    width-dependent limits (trig’s large-reduction limits: 2^23 for f64,
    2^20 for f32). `Object::test` stays the Boolean-result formula for
    comparisons, not this predicate. Native SIMD computes the common
    formula, then repairs flagged items with the object’s scalar routine
    before consumers use them; a proved-false predicate (part 2) needs
    no test or branch. Checked numeric validity is an Ops Boolean
    predicate paired with its error kind, shared by native and JIT
    execution; no predicate means total. The builder derives a step’s
    potential to record a data error from these facts, its mode,
    descriptions and range proofs; unknown means potentially raising;
    Select case planning combines this property over its steps, with no
    separate validity evaluator. Shared formulas over `Ops` are the
    default for elementwise arithmetic; a substantial algorithm can
    instead be 1 object-owned `Call` through the same builder and
    runner, its common computation never duplicated in a formula and a
    legacy kernel. Native and compiled kernels instantiate the same
    width-specific formula over their `Ops` (recipe). The numeric Kernel
    interface keeps every supported machine kind for each operation,
    independent of block/JIT admission: Boolean, fixed integers,
    ordinary machine integers, f16/f32/f64 (f16 works in f32), native
    complex. Host exact arithmetic stays outside this machine interface,
    using the same canonical mathematical facts, not the legacy kernel
    engine. “Numerical algorithms” specifies the shared bodies. Defined
    forms call other primitives through dispatch (part 5). Not:
    kind-specific rule files; parallel facts tables (recipe bodies);
    name checks replacing facts; duplicated common scalar math beside a
    formula.
7.  Rewrite engine (`program/rules.rs`; tinygrad `PatternMatcher`). 1
    table of BPL pattern rows and structural rules (fold of a copy,
    contraction, sliding fold, copy commute). A rule: a root key (data:
    step kind, root primitive) that rejects; 1 match giving bindings; 1
    builder from them. Rules run in the scheduler’s pipeline on
    `Program`s after `∂` and batching (XLA’s simplifier after JAX’s
    transforms), a lone node’s read included (part 8): the hidden
    specials they introduce (partial sorts, ranks, `log1p`, `expm1`)
    need only plans and kernels, and a pattern also matches its
    primitives’ batched forms, which carry an axis. Every pending value
    has its origin, so a rule matches in every spelling: `⍟1+x` →
    `log1p` from 1 row, across statements, bindings, dfns and trains; it
    doesn’t match across a read of its intermediate, which replaced that
    node with its result (part 3). A rule may change last bits, never
    leave the documented accuracy bounds (kmeans’ cancelling rule is
    out: decision 14); each has a model-independent reason and a
    reference case. The table: selections of a grade or sort (`k↑⍋⍵`,
    `↑⍋⍵`, `i⌷⍋⍵`, `i⊃⍋⍵`, for `⍋ ⍒ < >`) as partial sorts; `⍋⍋⍵`, `⍋⍒⍵`
    as ranks; `⍟1+⍵` as `log1p` and `¯1+*⍵` as `expm1` (accuracy); a `+`
    fold of `×` whose operands read through affine maps as 1 product
    (index notation’s level 3); a fold of a copy that repeats items as a
    fold of its source; `J+.×v` as a JVP and `u+.×J` as a VJP, where `J`
    is a Jacobian `∂` built and nothing has computed. Not rules:
    mandatory marked-form semantics (parts 5, 21); a combination of 1
    call matched by its function’s structure, which is its operator’s
    own algorithm (monadic `-.×`, `+.×`: `primitive_expansion`); Key and
    Partition, which keep the released grouping algorithms and normal
    batching (part 12; a native segmented group representation is
    deferred). Not: a test written twice (a pre-check beside the match);
    a rule tried from 2 places; 1 rule in 2 directions written as 2
    rules.
8.  Scheduler (forcing: `graph.rs`, `program.rs`, `program/run.rs`). 1
    pipeline, `schedule(&Program, &Running, from, runs) -> Plan`, then
    run preparation and execution, serves forcing on a miss, a trace’s
    end and replay planning for the rest after `Ran::Rest`: `from` is 0
    or the rest’s first node; the 1 `Running` state supplies bound
    inputs, callable/template bindings and computed prefix values, never
    reconstructing initial-only bindings; `runs` (expected reuse) feeds
    the compile decision. Scheduling gathers, rewrites, orders, fixes
    stage ends (tinygrad’s `indexing.py`) and outputs, plans legal
    cheap-chain recomputation and folds sharing stages; the cached plan
    holds this structure and kernel recipes. Run preparation makes cheap
    actual-count choices within the plan, outside item loops:
    compilation, contraction’s cheaper read, recomputation where
    producer/consumer multiplicity sets its cost; a choice changing an
    assumed kind or structure takes the guard/miss path (part 10). A
    read walks `Live::of` (part 3) and runs its graph’s plan from the 1
    cache (part 10); only a miss schedules, so a graph seen before costs
    a key, a probe and the run (cost model); a hit at unchanged lengths
    copies no description or map. A lone node is a graph of 1 (JAX’s 1
    compiled program per primitive and signature; tinygrad’s kernels
    cached by structure). Stage ends (tinygrad): a node whose length the
    data decides ends its stage, its count read before later steps are
    laid out; a value fuses with its 1 consumer, or with consumers
    indexing it alike; consumers indexing it differently store it unless
    it’s recomputed. Recomputable in each consumer’s stage: an
    elementwise chain over inputs and recomputed values, each node at
    most 1 copy in its row’s `cost` (XLA’s producer fusion,
    AOTAutograd’s recomputation); structural where item counts cancel,
    else run preparation compares actual producer/consumer multiplicity;
    coefficients retained by unrolled reverse AD follow the same rule.
    Past the graph, reference counts release each name and temporary at
    its last use. Select normalization supplies ordinary masked or
    compacted operations (part 14). Error recording and checkpoints:
    Measures; data errors never restart a read; descriptions are
    immutable. An uncompiled stage stores outputs in its own pass. A
    fold whose rows hold more than 1 item adds each row into column
    totals (NumPy `sum(axis=0)`), a compiled kernel into its piece’s
    totals; `inner == 1` keeps totals in registers. A fold whose totals
    feed later steps runs with them cell by cell where a cell fits the
    stage buffer (`fusion::BUFFERED`), the later steps reading its
    totals and items from cache; the plan chooses this schedule, never
    the compile decision. Run preparation uses compiled code when
    available, else compiles the selected recipe once actual items ×
    passes saved × saving per item × expected runs (loop counts, a
    recording’s reuse) exceed the compile cost; meanwhile the same stage
    executor runs its blocks uncompiled. Forcing reads, the 1 list:
    display; conversion to Python or JavaScript; writers (`•json⁻`); an
    observing read (a guard’s predicate, a count or length, an item read
    one at a time, `⍎`’s text); a call’s read of its own operands (part
    5’s roles: a parameter, values deciding a length or a
    value-dependent kind; selective assignment’s positions, part 16; a
    call on host values, part 5); an effect, run at its call outside a
    recording (what it reads, after the effects before it); the memory
    limit, in bytes: a pending graph whose kept-alive leaves (buffers
    only it still references) pass `MAX_KEPT` (8) times its largest
    array, or whose own nodes pass 1 MiB (about 7,000 nodes; one core’s
    L2), never a count of calls. A top-level statement’s end and
    destructuring force nothing. Running a graph and reading its result
    are separate steps (tinygrad’s `realize()`, `numpy()`): on a GPU a
    run submits work and a read waits for readback. Not: a 2nd scheduler
    or general multiversioning framework; eager executors beside it; a
    step routed around it to its kernel by kind; lone-call paths beside
    the pipeline; a plan cache beside the 1 cache; a session registry
    forcing pending work at effects; executor arms calling own code;
    masked-error retry state, revised-origin decisions, forwarding,
    protected retry roots or carries; replay of completed effects or
    random counters.
9.  Block stages, JIT (`program/blocks.rs`, `program/blocks/`, `jit.rs`,
    rustymath `gather.rs`, `agreement.rs`, `reduce.rs`). A stage runs
    its steps a block of items at a time and compiles by part 8’s
    formula. Rustymath’s 1 numbered-source `ReadPlan` serves kernels,
    folds and block stages alike (periods, tiling, runs): it routes
    `(source, position)` through positional mappings and binds run-time
    source views once, scalar fallbacks included: a fallback reads
    another scalar source; mappings hold no BPL `Value`; binding never
    mutates a cached map; the consumer supplies its run length. Lowering
    reads the object’s common formula and repair predicate (part 6):
    unless ranges proved the predicate false, the JIT branches only when
    a vector has flagged lanes, calls the object’s scalar repair for
    those lanes alone, and makes the corrected result available before
    downstream fused operations; there is no JIT-specific repair
    threshold or mathematical implementation. `jit::KERNELS` owns
    compiled code, keyed by emitted structure, types and modes, never by
    counts, which are run-time arguments. Run preparation compiles on
    demand only the useful recipe the actual counts select (part 8’s
    formula), never every possible version; different counts share code
    unless they select different emitted code; a selected version keeps
    its shared handle (`Arc`) after the first lookup, and plans hold
    references to that code, never another code cache or copy. Block
    stages read Booleans, fixed integers, ordinary machine integers,
    f32, f64, and characters as `i32` code points
    (`characters::as_codes`, no copy); a step giving characters, f16
    steps and complex steps run on rustymath’s whole-array kernels
    outside block stages, and a chain doesn’t fuse across them. Kernels
    preserve kinds, explicit conversions, exact results and the
    documented accuracy bounds; they may reassociate fold or product
    additions and use FMA for `a×b+c`, overflowing differently near the
    range limits; last bits, infinities and NaNs, and zero’s sign need
    not agree across routes; a dfn fold keeps its specified order. The
    JIT generates code with Cranelift, natively only (the browser runs
    block stages uncompiled until the GPU stage); LLVM is never a
    target; the built-in JIT works without an external backend (part
    23). Not: a stage’s own cache of compiled code; kernel sightings as
    a compile rule; basedpl copies of rustymath’s read planning;
    thresholds per consumer; a primitive → machine-op table; fold
    admission re-derived beside row facts.
10. Traces, recordings and cache (`program/trace.rs`, `program.rs`,
    `program/run.rs`). A trace records the graph that actual calls
    build; it is no dispatch mode or numerical evaluator. Inputs enter
    opaque and unforced. Captured free data, operator data and records
    are snapshotted once at the call and traversed with Trees; replay
    and deferred execution bind these current inputs, never rereading
    names or comparing separately kept free values. Callable templates:
    part 19. A recording holds the ordinary Program as a data-free graph
    template, with its key and legality guards. Binding a cached call
    instantiates that template with current data through shared node
    construction, without loaning or refilling a native execution Plan;
    only a read schedules and executes the graph; case admission
    inspects that same instantiated graph, not another native plan. 1
    Programs store owns function recordings, stops, Select plans and
    forced-graph plans: dfns key by their shared Definition, weakly
    owned, never by closure identity; forced-graph entries without a
    function owner are evicted least recently used past 256; the JIT
    code table separately owns compiled code, not plans.
    Description-backed planning for Select and typed-body admission uses
    the same ordinary call boundary on unbound native inputs with real
    descriptions; reading their items ends planning. Typed-body
    admission reuses the same key, capture snapshot and guard checks,
    not a private recording lookup or capability store. Unknown host
    structure ends recording before the normal host call runs once,
    never creating a fake scalar/empty-layout metadata Value. The read
    chokepoint distinguishes Count, Structure and Value reads and their
    input dependencies; unbound linear reads stay forbidden;
    description-only references neither execute nor capture callable
    contents. Recording stops, by the ordinary reason, at
    argument-dependent structure or control, host-kind data, guards,
    recursion, execute, error guards, outer writes and binding/settings
    effects; a body whose recording can’t represent or serve it still
    builds the same graph through the same operation entry. Broader
    cache admission, under arbitrary older levels or for every
    host/function-valued result, is a scope question: it needs a
    demonstrated and discussed common-use cost or behaviour, never extra
    protocols. Keys hold structural origins, literal values, function
    structure, numeric input kinds/layout facts (opaque inputs by kind
    bounds: part 2), axis equalities and parameter ReadKind. Data-input
    axis lengths key by 0/1/more class, with no buckets; array counts
    (take/reshape/window sizes) refill at concrete lengths through the
    row factory; rank, axis, permutation and other structural parameters
    key by value, as does a count deciding how many calls unroll, never
    by length class; data-dependent control that can’t be keyed safely
    stops recording. A hit binds current inputs, checks key and legality
    and refills changed descriptions and maps, never gathering,
    rewriting, ordering or placing stages; invalidated kinds, widths,
    layouts, structural parameters, equalities or proofs take the normal
    miss path, never speculative contracts. Compiled code stays
    count-parametric. A retrace limit of eight counts genuine signature
    changes. Data-dependent array lengths: visit Count roots in
    chronological Parameter order, schedule only through the next
    unobserved count, extend the same cache key and return `Ran::Rest`;
    the same Running prefix keeps completed effects and draws; a hit
    refills the suffix, a miss schedules it. Drawn axis names are
    numbered by first appearance and rebound to current names on replay.
    Output effects and draws with described results record normally.
    Not: a 2nd recording builder, store or cache; live closure data in
    cached programs; late free-name reads; numeric input range
    contracts; synthetic metadata Values for output reconstruction;
    generic residual count ports, an output count-index field or Counted
    multi-output metadata for loop residuals; retry or replay of
    completed effects; speculative kinds; another length type or
    bounded-shape framework.
11. Levels (`transform.rs`; JAX trace stack). Derivative and Batched are
    the 2 carried-value kinds. After semantic forms resolve, dispatch
    picks the newest level among the real data/callable dependencies; a
    level’s rule calls ordinary operations through dispatch. Levels
    nest, and nesting stays correct: Each under AD, AD under Each,
    higher derivatives, nested batching. An older level’s value is
    shared at the newer level and passes through to its own rules (JAX’s
    lift). The running batch level is the innermost; a carried batch
    identifies its level by id; an active resource call reaches the
    innermost running batch level not already handling it, even when
    numeric operands are shared. Frame geometry comes from current
    descriptions, not cached first-run lengths. Framed bodies begin a
    normal nested Batched level; guard cases split the current run’s
    cells. Cache reuse under every possible nesting is a separate
    optimization, never a reason for another carrier, serializer or
    execution protocol. Not: level data in storage; a separate evaluator
    for nested maps; widening a live batch run; a correctness refusal
    merely because a batch or derivative is already active.
12. Maps (`batch.rs`, 1 map entry; JAX `vmap`). Each, Rank, outer
    product, stencil, `f.g`, Key and Partition share 1 batching
    mechanism: dense cells run the function once on framed values under
    `Batched`, and primitive rules build ordinary graph operations. A
    framed value keeps the frame axes it depends on, length 1 where
    shared (JAX’s `not_mapped`). Draws and compacting masks cover the
    full frame before use. Nested or varying-shape cells keep the
    released mixed-array behaviour, through the same function-call
    boundary where they can’t batch; their numeric leaves still use the
    graph. Key/Partition grouping stays correct with no native segmented
    storage kind. Dfn folds/scans keep independent-lane batching (part
    13). A body returning a drawn axis name runs cell by cell and
    returns a nested result. Not: a per-operator evaluator;
    primitive-name checks in batching; segmented leaf kernels, segment
    roles or per-consumer packing protocols; new ragged execution
    machinery without a demonstrated workload need.
13. Host iteration (`eval/operators/power.rs`,
    `eval/operators/folds.rs`; released Power and dfn fold/scan
    algorithms). Power, dfn fold and dfn scan, resolved by
    `OperatorInfo`, apply their actual function values in 1
    language-level iteration home, reusing the released count,
    count-array, negative-count, predicate, convergence and history
    algorithms. Each body call takes the ordinary bound function entry,
    keeping current origin, captures, mode and settings, and can reuse
    its recording from the 1 cache. Under AD the actual body calls build
    ordinary primal and linear graph steps as execution proceeds, as the
    released unrolled AD did. A count array keeps the initial state at 0
    and the requested states in their original order; inverse counts and
    negative infinity apply the inverse body. Infinite Power uses
    `Numeric::convergence()` and compares a whole logical carry, not
    independently frozen numeric items. Arbitrary dfn folds keep
    right-to-left calls; arbitrary dfn scans keep left-to-right calls,
    their seeds, identities, empty cases and output structure.
    Independent lanes batch through the ordinary map entry. A body
    admitted by part 19’s shared typed-body query uses the same
    primitive facts, identities, optimized Fold steps and algebraic
    floating-point freedoms regardless of its spelling. Where the
    primitive permits reassociation, as `+/` does, cancellation can
    change more than the last bits; substitution adds no freedom to
    reorder non-associative operations or effects. Arbitrary bodies
    retain ordered calls. Empty-fold admission derives scalar argument
    descriptions from the existing kind, never a sampled cell or
    allocated dummy array. A count deciding how many body calls unroll
    is structural (part 10): array-length Count refill never lets an
    unrolled three-call graph serve four calls. A data-dependent
    predicate reads primals and follows the ordinary recording-stop
    rules, still computing the right derivative of the executed path.
    Histories use ordinary compact/mixed assembly; their language
    behaviour isn’t deferred. Effects occur on every actual body
    invocation. Not: `Step::Loop`, count-1 wrappers or loop IR;
    loop-specific JVP/transpose; compact-loop AD, primal/residual/linear
    body splits, carry-and-residual-count packets, history-axis metadata
    protocols, checkpointing; a native iteration compiler or repeat
    kernel lowering; a 2nd iteration runner; restoring the old Linear
    evaluator.
14. Selects (`program/control.rs`). `⍚`, `f@[p]` and guards on framed
    values normalize at the semantic-form boundary, before levels, into
    ordinary graph operations. Case admission takes the single cached
    planning route from descriptions (part 10), planning cases on whole
    arguments. Masked execution needs item-local dependencies, no
    effects and 1 common output layout and kind; under dynamic kinds, no
    value-dependent result kind; under checked dynamic execution, no
    case able to record a data error (part 6). Otherwise compact from
    the start. Static graph-numeric cases need no data-error admission
    check. Ordinary Choice/Picks assemble masked outputs; the selector
    is run-time data, not differentiated; Choice is jointly linear in
    its choice values. Compaction gathers each case’s selected cells,
    calls the original bound function once on that whole group and
    scatters its results; a case may reduce its group and fill the
    selected positions, with no implicit per-cell map; a mask with fewer
    axes selects whole cells. Under `∂`, gate each active argument,
    capture and tangent direction before calling the original bound
    function through the normal call entry:
    `select(mask, x, stop_gradient(x))` for primal inputs, Choice
    against zero for directions, derivative-marked Pending directions
    included (JAX’s double-`where` protection against an inactive
    infinite slope). Planned primal case bodies establish admission but
    never replace actual calls under AD, which must honour attached
    derivative rules. Both forms use ordinary graph AD and
    `linear::read_back`, accumulating at repeated positions; reverse
    uses the actual forward’s coefficients, never replaying a case to
    rediscover its branch. A selector read deciding structure follows
    the normal recording boundary, with no exemption from acceptance or
    cost requirements. GPU lowering can later use a per-lane branch.
    Not: an opaque Select Step or executor; Select-specific JVP/VJP
    wrappers or capture-gradient projections; a 2nd case planner;
    admission by spelling or narrowed ranges; masked-error retry;
    cross-Program case splicing beside normal function invocation.
15. `∂` (`derivative.rs`, `linear.rs`; JAX `jvp`, `linearize`,
    `transpose`). Derivative levels and tags give forward, reverse and
    higher-order differentiation; tags, escaping-value stripping, custom
    JVP rules (`f∂←g`), `•detach`, nested AD and batching stay
    supported. Differentiated numeric leaves promote to real-floating
    kinds by the normal float-width rules, and seeds take the promoted
    input kind; parameters and nonnumeric structure stay unchanged;
    complex AD stays unsupported until its convention is agreed.
    Primitive JVPs come from rustymath partials through rows;
    zero-tangent operations run on primals in 1 dispatch rule. Linear
    nodes are ordinary graph steps over unbound seeds, their derivative
    level on the node, not its description or cache key. Forward mode
    binds a seed; reverse mode transposes the ordinary linear steps in 1
    pass using row linearity facts. Reuse the released argument/parts
    traversal and Jacobian reconstruction, not its separate Linear/Op
    evaluator. A Jacobian uses the normal map entry over unit seeds,
    choosing rows or columns from actual dimensions. Ordinary graph
    coefficients keep the actual intermediate values the derivative
    needs. A recorded linear function walks the ordinary graph bounded
    by its derivative level: nodes outside that level become inputs
    holding the actual current coefficient Values, their pending graphs
    and older levels included; current seeds are argument inputs. Body
    calls made by Power and dfn folds/scans unroll into these same steps
    (part 13). A shared frame input uses normal spreading, whose
    transpose sums across the frame. 1 `linear::read_back` transposes
    numbered-source read plans, reading where positions are unique and
    accumulating where they repeat; copies, joins, selections and fills
    share it. Select input gating follows part 14. Unbound linear reads
    are refused at the read chokepoint. Native floating replacement and
    additive writes differentiate through the same At and read transpose
    rules: positions are nondifferentiated primal choices; replacement
    requires distinct destinations; addition accumulates every repeat;
    broadcast cotangents reduce to the original RHS shape. Structural,
    shape-changing and host-kind writes have no derivative and raise
    Unsupported when differentiated; unrelated nondifferentiated writes
    stay ordinary work. Not: a separate AD evaluator; a separate
    primal/linear Program partition, coefficient reconstruction, tape,
    registry or packet codec; residual-history outputs, reverse-loop
    programs or checkpoint passes; loop-specific derivative body
    wrappers or packing; linearization by replaying the user’s function
    to rediscover its executed branch; entry-by-entry Jacobians; Rust
    closures standing in for BPL functions.
16. Writes (`selection.rs`, `eval/assign.rs`, compiled targets in
    `eval/binder.rs`, `eval/tree.rs`). Supported element writes,
    whole-item replacement and nested selection composition compile
    through 1 mechanism; adding identity, enclosure or another supported
    lhs composition never stops recording or reruns the selector in the
    interpreter on replay; any speed exception needs Jeremy’s agreement
    on the concrete feature boundary and an efficient alternative. 1
    selection owner keeps `g`’s read recipes through the pending update
    and composes them back to the root in the same Program; ordinary
    reads use computed-value caches, address demand the retained
    recipes. Each affected numeric leaf uses `At`, inside and outside
    traces; Mixed containers and output Trees rebuild the outer
    structure from the updated children; whole-item replacement
    substitutes a child value without a numerical write; the scheduler
    chooses in-place execution. Direct paths (`⌷ .[] ⊃` chains) are
    compositions inside the walk. Key extension (`m.k←v`, a missing key)
    rebuilds the containing structure with the new item. Positions
    (`f@[p]`, a mask) are a selection by `p`, composed into the walk. A
    `g` that computes rather than selects writes through `g⁻`; a `g`
    that adds items (`{⍵,0}`) raises “cannot assign to an item that
    isn’t from the original”, while a catenation of selections works
    (`10@{(1↑⍵),¯1↑⍵}`, `((1↑a),¯1↑a)←8 9`). Nested selections use
    logical item paths and numeric-leaf reads; logical source paths
    identify whole native children; native read views stay element
    selections even over a whole leaf; general nested-to-flat selector
    construction uses the shared structural-read boundary. At a repeated
    position, which value a replacing write keeps isn’t promised, keyed
    or not (decisions 6.2, 15); an accumulating write (`y.[dst]+←v`)
    adds every repeat. Checked selective assignment validates every
    position before changing anything, so an error leaves the name
    unchanged; static graph-numeric writes skip invalid numeric
    positions instead; structural and host-kind errors stay checked
    (Measures). Positions force; values may stay pending; a trace checks
    positions when its `At` runs. Writes into exact-rational or function
    leaves are explicitly unsupported in this release; native-to-boxed
    replacement works through the host-kind recording stop. Computed
    whole-Mixed-root writes can’t change the outer shape or keys (use
    ordinary root assignment); child replacement may change internal
    shape or keys; selected outer positions don’t fix their children’s
    internal shapes. Not: a direct route beside the walk; labels;
    numerical write loops in structural code; write executors or
    numerical host writers beside `At`’s; appends chosen by spelling; a
    segmented entry/leaf address protocol; a 2nd selector evaluator or
    interpreter fallback.
17. Effects, draws. Effects and draws are always nodes, inside and
    outside traces. Every effect (output, system effects, time, input
    `⎕`) is an ordered node: effects run in order among themselves, each
    taking the token of the effect before it, and an effect forces only
    what it reads (JAX’s ordered effects; when other pending work runs
    isn’t promised); a stage ends at each effect node. Outside a
    recording every effect runs at its call, after the effects before
    it; a call whose body holds an effect is itself an ordered effect;
    host iteration invokes effects on every executed body call, in
    order, with no cached loop node. An effect that changes bindings or
    settings, or gives a value read as structure (input `⎕`, `•load`,
    `⍎`, settings changes), ends a trace’s recording; pending nodes keep
    the settings they were built with (part 4). Effect kinds: `Acts`
    (output, input `⎕`, system functions that read or write files, the
    network, the clock or the terminal, or make something new on each
    call: `•delay •nput •fetch •nget •time •load •js •uuid`); `Catches`
    (an error guard, which forces the work built inside it at its exit;
    a call on prototypes); `Writes` (a write to a binding outside a
    level’s frames). `⍕` is pure: a carried argument goes to its level
    before a step; `∂` formats primals (a zero-tangent call); batching
    stops and formats cell by cell. A draw is a `Draw` node, computed
    when a read reaches it; map nesting comes from the levels. The
    origin captures 1 opaque generator handle as a data input, its state
    owned by rustymath’s `random.rs`; its key and counter are run-time
    data, never structural parameters; capturing or binding a generator
    never advances its counter; `•prefs` with a seed changes the
    generator key. Each whole `Draw` reserves 1 stream from its captured
    generator when it executes; its first read computes the whole draw,
    which later whole or partial reads reuse; a replay executes a new
    draw and reserves a fresh stream; deferred read order can decide
    which draw receives which stream. A batched draw enumerates the
    whole result in canonical flat item order through rustymath’s
    `Items::new` stream, each item with disjoint word positions and
    rejection space; frame geometry determines result placement. Equal
    seeds don’t promise equality between different batched/unbatched
    spellings. Not: host draws or effects beside their nodes; a session
    registry forcing every pending value at an effect; draws stopping
    traces; replays repeating draws; a 2nd home for a generator counter;
    a draw context beside the levels; a 2nd seeded generator; another
    RNG hierarchy (a special first-cell stream, cell-stream fold or
    separate cell rejection mapping); a body-upfront draw count, c+k
    place or replay counter reservation.
18. Binder, executor (`eval/binder.rs`, `eval/tree.rs`). The binder
    compiles each statement before it runs, emitting instructions only;
    `tree.rs` is the 1 statement evaluator and holds the value
    constructors, through which the compiler may build values from
    constants (constant folding: `⍤1` derived once): construction, not
    evaluation. A top-level statement compiles, then runs once. Numeric
    tokens are interpreted under the active Numeric when their
    containing statement runs; a bound value can therefore be
    constructed before a dfn’s body literal. Keep that single timing
    rule rather than adding literal environments or rewriting shared
    Definitions when functions are constructed. Binding rules: (1) a
    call’s result is a value to the binder, as is a field read from it;
    a subscript’s selection counts as a call: `+/÷(f 0)` is an
    expression; `g←f 0 ⋄ +/÷g` names a returned function; `1⍚fs x` calls
    a selected one. (2) A role change the compiler can’t see is an error
    (decision 10), never a restart: a write that changes the role of a
    name outside the writing function’s frame (raised at the write), and
    a read, later in a statement, of a name whose role that statement’s
    write changed (raised at the read); an array binds as a function
    when any item is a function; `Session::assign`, the 1 home of
    assignment, compares old and new categories. (3) `⍎` changing a
    name’s role while a statement that reads it runs is an error
    (decision 9); an unbound name binds as an array (`x+⍎"x←3"` works).
    An earlier assignment in the statement (`x+(x←3)`) gives the name
    the assigned expression’s role for the rest of it. Roles are read
    again at each statement’s start; a recording checks the roles of the
    names it reads at the start of the statement and of each group or
    run. The compiler binds ahead: dot access on a name (the field’s
    role from `T`’s value, a recorded fact); a scope `T.(expr)` (from
    `T`’s keys and field roles; a computed `T` compiles its body on
    entry, as `⍎`’s text does); superscripts; numeric subscripts by
    their operand’s role (`(⊃fs)₁` selects); an absent `⍺` (compiled per
    valence, or checked at the statement’s start). Using a call with no
    result as a value fails when it runs. Not: evaluation inside the
    binder; resume machinery; a 2nd node evaluator; a 2nd reader of the
    grammar.
19. Function values (`eval.rs`, `eval/operators.rs`). Leaves: primitive,
    system, dfn, late-bound name and function array. An operator
    application, primitive or dfn operator alike, is 1 Applied variant
    holding its operator and operands; trains are 1 variant holding
    their tines, and a train’s array tine stays ordinary data. Attached
    inverse/JVP rules belong to FunctionData beside its environment.
    OperatorInfo owns call, semantic-form resolution, operand
    classification, spelling, inverse, discards and Python name;
    declared fold/product operations and bitwise resolution use this
    table, not parallel switches in Function. Function dot/field/index
    reads are operator kinds with their selector parameter. Framed is an
    internal function kind for ordinary batching. A Recorded function
    references an ordinary Program and its current input bindings, never
    a compact-loop body or residual packet. 1 traversal, the released
    shared Rebuild/visit_operands, serves capture, template, rebuild and
    attached rules through its actions; 1 OperandKind rule tells
    structural operands from data, ranks included;
    callable/data/metadata roles are classified before capture;
    description-only references keep their metadata without traversing
    functions they happen to contain. All executable free data are
    call-time snapshots in 1 capture order; FunctionOperand
    distinguishes actual values from positional inputs; a literal
    operand’s value is its literal node; a recorded body takes its
    captures and arguments as data (`Value::data`), and its key holds by
    value any literal node that still reaches an input. Cached templates
    strip run-time environment and context and hold no current producer
    or captured data; current bindings supply the data when called;
    actual function results rebuild once at the function boundary. Host
    results this representation can’t hold take the normal host
    boundary, never universal closure conversion. Nested AD and custom
    rules stay correct. Higher-order consumers obtain known primitive
    facts through 1 typed-body query at the requested arity and cell
    descriptions. Declared primitive and bitwise constructors use the
    constant-time base case. Other bodies reuse the existing isolated
    description-backed planning run and guarded Program cache. An
    admitted body has 1 plain native result from the exact original
    primitive call, direct ordered formal references with identity read
    maps, and matching numeric mode and declared settings. A conversion,
    wrapper, observed argument/control dependency, effect or custom
    derivative prevents substitution; consume the capture walk’s
    recursive custom-derivative fact rather than adding a traversal. All
    nonliteral native captures become the existing description inputs
    before planning; nonliteral host data or structural captures
    conservatively decline before the body runs. Literals, callables and
    settings retain their established provenance. A declined query uses
    the ordinary function and iteration process. No new IR, capability
    cache, guessed identity or per-consumer dfn recognizer is permitted.
    Not: per-wrapper FunctionNode variants; attached-rule wrapper nodes;
    duplicate child/name/rule traversal; live-name replay reads; a 2nd
    template IR or registry; loop-specific Recorded input/output
    projections; Rust closures as BPL bodies; another call route for
    trains.
20. Index notation and positions (`⍳`, `•index`, rustymath selection
    planning). Level 2: regular selectors as strides, in rustymath’s
    selection planning; level 3: contractions as 1 rule (part 7).
    Positions count from 0, negative ones from the end; a numeric
    position outside its axis raises INDEX ERROR under checked dynamic
    execution and reads the fill under static graph-numeric execution,
    an empty axis included (Measures); a NaN position reads the fill in
    either mode. Missing keys and other structural errors resolved at
    the boundary stay checked (keyed agreement’s fill: part 2). `.[]`
    takes no `⍠`; a fill comes through `⌷⍠["fill":v]`. Drawn names: part
    10’s key; a map whose body returns one: part 12. Axis order of
    `g×z`: the right’s order where every axis of the left operand pairs
    and the right has more axes; otherwise the left’s axes first. Not:
    range inference (`•index ∞`, decision 11); strides decided from 1
    run’s index values inside a recording.
21. Scripts and marks (`number.rs` `MARKS`, `syntax.rs`). Marks, 1 table
    (`MARKS`): `ₓ` ordinary machine integer, `ᵣ` unbounded
    integer/rational, `ₓ₁ ₓ₂ ₓ₄ ₓ₈` fixed widths, `ₕ ₛ ₚ` floats of 16,
    32 and 64 bits, `ₐ` real; after a function each is an operator kind
    (part 19), after a value a conversion node (`Mark::conversion`). A
    function mark normally converts each result as its producer’s stage
    stores it: `⌊ₓ₁`, `-ₓ₁`, `÷ₛ` and `⍳ₓ₁` take no 2nd pass; other
    marked functions convert their result. A marked literal group, or a
    superscript on a literal number (`3²`), folds through the same
    conversion or call (part 18), never in the lexer or parser. `ᵣ`:
    `5ᵣ` is `5ᵣ1`; a trailing `ᵣ` converts a value into the unbounded
    domain, a finite real float exactly from its stored value, without
    recovering decimal intent; denominator 1 stays in that domain and
    displays as `3ᵣ`, never `3ₓ`; `xᵣy` floors its components without
    machine narrowing and uses the strict rational-construction
    division; ordinary `÷ᵣ` follows the call’s Mathish policy;
    Python/JSON conversion rules stay: already-unbounded external values
    enter this domain without a BPL annotation. `ₐ` gives real floats at
    the arguments’ float width, never a width chosen from values:
    `ₓ₁ ₓ₂` promote to float32, `ₓ₄ ₓ₈` to float64; a weak literal takes
    its partner’s width; no compact join takes the default width.
    Outside the real domain it gives the principal value’s real part
    (`√ₐ¯4` → `0`, `⍟ₐ¯1` → `0`, `¯8*ₐ÷3` → `1`). Result semantics other
    than division: `fₐ` ≡ `(f args)ₐ`; division selects float arithmetic
    before computing. The marked-form kind entry resolves early, before
    the unmarked producer’s kind rule, operand reads or level handling
    (part 5): the primitive forms `√ₐ *ₐ ⍟ₐ ○ₐ` and inverse-circle real
    forms to rustymath’s real operations (`RealSqrt`, `RealLn`,
    `RealPow` and `RealCircle`‘s code table); machine-integer
    `⌊ₓ ⌈ₓ ÷ₓ`; float division `÷ₐ`; rational division `÷ᵣ`, which
    selects the exact domain before the division, never converting its
    floored result (integer `÷` already floors). Each real object holds
    its real form, the real part of its complex form, formula
    (`Facts::formula`), range rule and real-part slopes: `RealLn` has
    1/x; `RealPow` below 0 differentiates |x|^y cos πy; `RealSqrt` and
    circle codes whose real part is constant outside the real domain
    have slope 0 there; infinite slopes use JAX’s double `where`.
    Resolved operations use normal dispatch and the objects’ derivative
    facts; their origins keep the marked call. These mandatory semantics
    are not late speed rewrites. No kind is speculated (part 2); the
    listed primitive real forms, ordinary integer `÷` and `÷ₓ` are
    guaranteed fast. Numeric superscripts on functions mean Rank: `f¹` ≡
    `f⍤1`, `f⁰ ¹` ≡ `f⍤[0 1]`, `f⁻¹` ≡ `f⍤¯1`; one, two or three signed
    entries use Rank’s normal rules. Bare `f⁻` is inverse; `f⁻ ¹` ranks
    the inverse. Numeric-array superscripts remain powers, including
    `x⁻¹`. Iteration uses `⍣`. Letter superscripts: `ᵀ` transposes an
    array; `ᵘ` makes a scalar holding a function; either on the other
    role is an error.
22. Reports (`explain.rs`, rustymath `events`). `•explain` reports every
    graph, with no size filter; users filter by `items`. Records:
    `fused` (stages run); `stored` (every value a stage end stores
    partway, with a reason); `forced` (pending work that a read forces
    partway, with a reason: an observing read, a host kind, an effect’s
    operand, a selective assignment’s checks); `programs` (every
    recording: ordinary iteration bodies, maps’ operands, `∂`’s
    operands; events include “retraced” and “rerun”, as JAX’s cache-miss
    explanations and Dynamo’s recompile logs report); `stopped` (a row
    per reason with calls, items and `site`, as Dynamo’s
    `break_reasons`); `fast` (each rule applied); `fallbacks` (every run
    per item, cell or iteration on the host, with reasons); `declined`,
    `accumulations`, `mixed`, `compiles`, `routes`. `Facts::reads`, 1
    home, classifies a reader that takes its argument whole (sorts,
    grades, searches, `⌹`, products). Routes are rustymath’s `events`,
    their 1 home, which basedpl’s `•explain` and rustymath’s Python
    `rm.explain()` both capture; the route list holds only routes the
    target keeps. Route checks and the coverage matrix keep 3
    requirements: a mapping stage compiles (`compiled`), a fold reads
    its argument inside its stage (`fused fold`), a gradient replays
    (`trace`); `⍝⍝⍝ route` names only these 3, and every other route is
    checked by the absence of slow events (`fallbacks`, `stored`,
    `forced`, `stopped`), which fail unless allowed. Reports and
    diagnostic tools change without agreement.
23. Later stages’ requirements on the core (not built in this release).
    GPU: running a graph separate from reading its result; nodes as
    plain data; transposes that read rather than scatter where they can;
    readbacks for results whose shape depends on values; bf16 as a kind
    (spelling open); WGSL has `bool i32 u32 f32` and `f16`, no f64, i64,
    complex or bf16 (those stay on the CPU or are emulated: the GPU
    stage’s choice); checked data errors gathered into 1 flag read once
    at the end (the session slot’s device form; XLA’s error bits);
    buffer capacity independent of plan keys, with captured graphs
    reused while their buffer addresses stay valid and recaptured when a
    capacity change requires it; user bounds for lengths the data
    decides (JAX’s `size=`); kernels that read counts from device
    memory. Array files: leaves backed by buffers the allocator doesn’t
    own: rustymath `Buffer`’s owner `Owned(Vec<T>)` or
    `External(Arc<Region>, offset, len)`, read through `&[T]`, copied on
    its 1st write; a region says whether the host can read it; built
    with array files. External backend (mo): the graph as plain data
    that a backend writes out.

## Interfaces

These are target interfaces, not promises to preserve today’s names.
Each shared fact has one producer, a construction boundary and a
consumer API. A coherent change names the superseded entry points and
duplicated decisions it removes. Use private construction or types where
callers become simpler. Use a narrow ratchet for remaining escape
hatches when static enforcement would spread obligations. Pin concrete
declarations for the next coherent chunk under the build process above.

- `Desc` (part 2): `Desc::of(&Value) -> Option<Cow<Desc>>` is the 1
  projection: pending borrows its description, Batch its cell, Dual its
  primal’s; stored native leaves use `Desc::leaf`. Native pending
  outputs have concrete descriptions after their required Count reads.
  `None` is a genuine host value, whose structure is borrowed from the
  actual current host input or read at the normal host boundary; absence
  never means scalar, empty layout or a fabricated numeric kind.
- `Outputs<T>`: flat leaves and optional Trees, keeping shy/no-result
  information; an empty tuple differs from no result. Descriptions use
  the same logical tree, with optional native Desc leaves for genuine
  host leaves. Map, bind, return and genuine tuple construction keep the
  packet as it is; a Value is assembled only to cross an actual
  language-function boundary, and that function’s result is flattened
  once. `Program.outputs: Outputs<Out>` owns the result topology, which
  binding maps directly; recorded functions keep no separate result-tree
  repair; Programs keep the effect-token root separately. No
  description→Value→description round trips or per-consumer packet
  codecs. The row factory returns
  `PlannedOutputs::{Described(Description), Observe}`. The output form
  owns observation admission, arity legality and normalization to
  control-root descriptors. An opaque host result can have zero
  described native leaves and a control root; this does not claim that
  the runtime result is an empty tuple. A valid opaque result, an
  unavailable native description and a lost operand must not share a
  stop reason.
- `Step` (part 4):
  `run::step(step, operation, reads, values) -> Outputs<Value>`.
- Observation (parts 1, 3, 5): the existing `Value::read` and runtime
  read boundary own observation. Item-processing helpers accept the
  existing storage view, buffer or slice they operate on. Retained
  native index tables implement RM `IndexSource` on `ArrayData`, not
  `Value`. Immediate Boolean mask decoding borrows `&[bool]` through the
  shared `rustymath_buffer::selection::boolean_selector`; scalar and
  array masks use the same validation. Graph construction keeps logical
  values and descriptions. Preserve the original graph and selection
  identity. Do not introduce a second stored-value representation, a
  proof wrapper or extra contexts passed through unrelated code. Default
  fills, chosen fills, prototype inference and logical write
  destinations remain distinct responsibilities.
- Numeric facts (parts 2, 5, 6): primitive rows own language result-kind
  policy. Rustymath’s mathematical objects own formulas, domains,
  numerical validity and backend capabilities. These are separate facts.
  Typed `Ops` recipes retain their fixed numeric algebra rather than
  repeat language kind inference. `Kernel::unary` and `Kernel::binary`
  set that fact in the existing private `typed_domain`; `Kernel::domain`
  owns result-kind admission, including already-Complex inputs.
  Consumers do not exempt Recipes or Complex inputs themselves. Native
  and compiled execution consume the same facts.
- Row factory (part 5): declared Count/Structure/Value parameters are
  observed by the shared call-preparation owner before semantic
  consumers or a row decode them; ordinary Data keeps its original
  logical handles and transform levels. The borrowed `CallView` exposes
  Desc, known Trees and authorized resolved parameters, never physical
  inputs or ordinary value reads, and accompanies the original Origin
  through transform metadata rules and batching callbacks. Geometry
  consumes the resolved classification; it does not infer it again from
  parameter values. Keep constructors private where that prevents
  reclassification without passing a new proof through callers.
  Function-body numeric helpers use the resolved NumericScope, keeping
  the original captured setting operands. Run-time host code borrows
  `RunView` for explicit logical reads. Origin reborrows and projects
  through the common implementation.
- Gather data indices (parts 5, 12):
  `GatherIndex::of(call: &CallView<'_>, position: usize, item: Option<usize>) -> Option<Self>`
  constructs an index only for `call.data_positions()`. Its fields are
  private to `program`. Geometry decodes resolved parameters into known
  routes before using this constructor. It cannot bypass the eligibility
  check with a struct literal. Read-relation code inside `program` can
  remap the ordinals.
- Dispatch (part 5):
  `Operation::dispatch(Origin) -> Result<Outputs<Value>>` is the entry
  and the caller of level rules and the builder;
  `call(args) -> Result<Value>` only constructs the origin and
  delegates; a level rule receives the complete origin and returns
  `Result<Outputs<Value>>` through dispatch.
- Levels (part 11): `Level { id, kind: Derivative | Batched }`;
  `Value::Carried { level: id, .. }`.
- Graph to runs (parts 3, 8): `Live::of(roots)`; `Program::of` on a
  miss; `schedule(&Program, &Running, from, runs) -> Plan`; `Plan::run`
  over the same `Running` state. The count frontier bounds description
  and refill work, never execution, and proves nothing about earlier
  execution: remaining work follows actual completion, resident values
  in the pool, completion tokens and published source results; released
  pure values are recomputed when demanded; completed effects never
  repeat.
- Rules (part 7): `Rule` has
  `RootKey { kind: StepKind, root: Option<Primitive> }`, 1 match giving
  `Bound` and 1 build through the shared program builder, which
  registers nodes, literal inputs and structure-only function templates
  with positional dependencies through the same bindings and the row
  factory over its borrowed call view. The table is indexed once by
  `RootKey`. A routes-off diagnostic uses the same graph and math
  objects with optional rewrites, grouping and compilation disabled.
  Required semantic normalization remains enabled. The inventory
  declares applicability for tests that inspect routes or storage and
  for resource-heavy cases. Do not add a second interpreter or semantic
  implementation.
- Function values (part 19):
  `FunctionNode::{Primitive,System,Defined,LateBound,Applied,Train,Array,Recorded}`;
  `Operator`: a primitive OperatorKind or a dfn operator closure;
  FunctionData: node, environment, optional attached rules;
  `FunctionOperand::{Value, Input}`; `Framed` carries batching geometry.
- Typed body facts (parts 5, 10, 19):
  `Function::primitive_body(&self, parent: &Origin<&[Value]>, arguments: &[Option<Desc>]) -> Result<Option<Primitive>>`
  is the shared query for higher-order admission.
  `declared_primitive(dyadic)` is its constant-time constructor base
  case, including declared bitwise operations. The declared base case
  also applies to host-kind cells without native descriptions. Other
  bodies require known native cell descriptions and use the existing
  isolated planning recorder and guarded Programs cache. The query
  exposes an exact original primitive call, not a sampled output or
  rewritten approximation to a body.
- Cache (part 10): `Programs::serve(base, current_origin)` checks
  discovered-read guards in the 1 function-structure store. 1 cached
  `Recording` holds the program, logical output description, effects,
  itemwise/error properties and discovered reads, serving ordinary
  invocation, case planning and actual function-body calls.
- Refill (parts 8, 10): `Plan::refill(from, running)`, from 0 or after a
  data-determined length, through origins and the row factory; the same
  bound inputs and prefix values supply parameters and callable bindings
  through the borrowed call view. A replay or cached graph read runs it
  when lengths or count parameters changed.
- Errors (Measures): `Session::error` holds rustymath’s first-error slot
  and count; `Session::raise() -> Result<()>` raises at checkpoints;
  runners compare counts around steps.
- Writes (part 16):
  `Scope::new(root)?.select(target) -> Result<Option<Walk>>`;
  `Walk::write(values, combine)` lowers structural replacement and
  native-leaf `At` nodes.
  `WriteTarget::Source { port, frame, distinct }` identifies logical
  destinations independently of the mathematical root. The `ReadPlan`
  carries positions, missing addresses and batching; no index buffer is
  built. A result relation composes those destinations through its data
  root’s physical read route; dynamic publication first reads the same
  relation in scalar validation mode.
- Test policy: the native reference inventory owns case identity,
  applicable modes, deadlines and skip reasons. Rust tests, worker
  checks and Python reports consume that inventory. Evaluation and its
  final lazy read share one execution scope. Skips retain their
  identities and remain unresolved.

## Numerical algorithms

General positive floating Pow/Root composes the shared Exp/Ln formulas:
`exp(y*ln(x))` and `exp(ln(x)/n)`. Preserve cheap constant/integer
powers, sqrt/Cbrt, exact arithmetic and zero/negative/parity/domain
semantics. Characterize float errors and document function-specific
exceptions without weakening common-use results or exact arithmetic.
Accuracy characterization uses `u*|y ln(x)|` (unit roundoff u) for Pow,
not the exponent alone; f32 and f64 need their own bounds.

Trig keeps short fast reduction and polynomials. Rare out-of-range lanes
use the object’s scalar platform/native sin/cos/tan repair instead of
porting custom Payne–Hanek u128/table arithmetic into Ops. The predicate
and its limits have one home (part 6); range-proof elimination belongs
to part 2, compiled repair ordering to part 9. Native/wasm provider and
ABI selection must preserve the semantic and speed requirements. GPU
execution needs device code or portable lowering, not necessarily
another mathematical implementation.

Reuse rustymath’s existing optimized elementwise, reduction and
contraction drivers through shared interfaces co-designed with BPL. Keep
repair and error reporting outside their common hot loops. Do not add
new SIMD code or a separate vector execution engine in the
foundation-writing and integration stages. Once the foundation works,
measure before changing performance code. Preserve non-FMA f32 trig’s
f64 intermediates through the existing width operations. A retained
algorithm that needs table lookup can use the existing scalar loads or
platform operations.

Keep Cbrt’s compact fixed algorithm: two f32 or three f64 Newton steps
and correction. Incomplete Gamma/Beta convergence computes the answer
and stays in canonical object-backed Calls where appropriate. Ordinary
Gamma/Beta do not require those convergence loops. Substantial
algorithms need neither a general numerical-loop IR nor a blanket
vector-call ABI. Host exact arithmetic and native f16/complex retain the
coverage in parts 1, 6 and 9; block admission does not decide
correctness coverage.

Shared formulas and object-backed Calls must each have one mathematical
implementation. An algorithm’s call/staging and lost-fusion costs belong
to the normal speed budgets, not an assumed fixed one- or two-copy
charge. Preserve the documented finite-result bounds until characterized
exceptions are documented. Performance and simplicity outrank accuracy
machinery beyond those requirements.

## Cost model

What each stage of the pipeline ought to cost, counted from the work it
must do and set beside the role models. Never from today’s tree: a stage
over its budget is a gap in that stage’s code. Estimates cover a
single-output call with at most 2 operands; cost variable operands,
output descriptions and argument trees at their actual sizes. Units:
instructions retired (`instructions`, Apple silicon) per call, node,
step or run; work per item in copies (DEV.md).

### Primitive operations

Assumed, Rust on Apple silicon, small sizes: - small allocation
(size-classed allocator) 40; free 30; `Arc` clone or drop 5 - hash of a
small key (≤32 B) 15; map probe, hit 40; insert 60 - indirect call 10 -
a `Desc` (kind, atom flag, layout of rank ≤4, range) 30; a kind, shape
or range rule 40-100 (2 operands’ agreement at the top) - a `Mapping`
built or composed 30-60 - work per item: elementwise f64 0.5-1 (2-lane
NEON, unrolled); copy 0.5; fold 1

### Stage budgets

<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr>
<th>Stage</th>
<th>Work it must do</th>
<th>Budget</th>
</tr>
</thead>
<tbody>
<tr>
<td>dispatch, 1 call</td>
<td>innermost level among ≤2 args (10); row by index (5); roles (10);
captured input binding (20 a captured leaf); kind, shape, range and
validity rules (100); node allocated and filled (70); interned: hash,
probe (60)</td>
<td>300 + 20·captured leaves</td>
</tr>
<tr>
<td>pending node</td>
<td>node and origin: about 150 B for a single-output call, plus variable
operand references, output descriptions and argument trees; leaves stay
alive, no intermediate is stored</td>
<td>0</td>
</tr>
<tr>
<td>read of a pending graph of N nodes, plan cached</td>
<td>structure key: each node’s step and operand descriptions hashed (30
a node); probe (40); per step: executor call (15), output allocation
(40), kernel setup including cheap actual-count choices (50), work; each
intermediate freed at its last use (30); <code>Live::of</code>’s walk
(20 a node) and allocation (50); leaves bound to the plan’s inputs (20 a
leaf); the intern table updated as each node is replaced by its result
or dies (30 a node)</td>
<td>150 + 235·N + work</td>
</tr>
<tr>
<td>plan, a cache miss</td>
<td><code>Program::of</code> and gather (30 a node); rewrite: root-key
probe (20 a node), match and build (200 a rule applied); order (20 a
node); stage ends (20 a node); descriptions and maps (130 a node); plan
allocated (50 a node)</td>
<td>270·N + 200·rules</td>
</tr>
<tr>
<td>refill</td>
<td>descriptions and maps at the run’s lengths through the plan’s own
builders (130 a node); no gather, rewrite, order, stage ends or compile
decision</td>
<td>130·N, only where lengths or count parameters differ from the plan’s
last refill</td>
</tr>
<tr>
<td>trace</td>
<td>the run’s own dispatch and reads, plus input roles and the key (30
an input); capture binding was paid at dispatch</td>
<td>dispatch + 50 a call</td>
</tr>
<tr>
<td>replay</td>
<td>key: all input descriptions hashed (30 each), inputs bound (20
each), legality guards checked; probe (40); refill where lengths or
count parameters changed; run as a read (135 a step + work)</td>
<td>150 + 135·N + refill + work</td>
</tr>
<tr>
<td>compile (JIT)</td>
<td>only the selected useful version where its formula repays (part 9);
code owned by <code>jit::KERNELS</code>, keyed by emitted structure,
types and modes, never by counts</td>
<td>per formula</td>
</tr>
<tr>
<td>tree op, dfn call</td>
<td>op 30; name load 10; dfn frame 60 + 20 an argument</td>
<td>—</td>
</tr>
</tbody>
</table>

Base budgets assume at most two input leaves and no additional captured
data; extra leaves incur the binding/hash work listed in each row. Role
models for the shape of these costs: JAX’s eager mode runs each
primitive as a 1-op compiled program cached by primitive and abstract
signature (`apply_primitive`); `jit` checks a cache keyed by the
function and its arguments’ signatures, so a hit costs a key check plus
the program. tinygrad builds every op as a lazy `UOp`, caches compiled
kernels by their AST, and TinyJit replays the kernels a real run
captured. BPL’s budgets sit below both: no Python, no device launch
before the GPU stage.

### What follows

1.  Small calls: a call then a read of its result ≈ 300 + 150 + 235 =
    685 + work. Every pending value keeps its node; a call matching no
    rule pays 1 root-key probe (20).
2.  Ordinary code against replay, a body of N calls run again at the
    same shapes: ordinary ≈ 300·N (dispatch) + 150 + 235·N (read, plan
    cached by structure) = 535·N + 150; replay ≈ 150 + 135·N. About 4×
    at tiny sizes; at 10⁶ floats a step’s work (about 10⁶) dwarfs both.
3.  Lengths: per-axis 0/1/more classes, with refill when lengths or
    count parameters change (part 10).
    - Run preparation uses actual counts for compilation, contraction’s
      cheaper read and recomputation when multiplicity matters. These
      cheap checks belong to kernel setup, outside item loops. The
      cached structural plan and code cache do not change for a new
      length alone.
    - Ordinary calls on a growing array reuse their plan within a length
      class and the plan’s guards. Refill plus replay is ≈ (130 + 135)·N
      = 265·N a step, including kernel setup. Scheduling every exact
      shape would cost ≈ (270 + 135)·N = 405·N a step. Existing buffer
      reuse remains independent of this key policy; no new growing-loop
      compiler is required.
    - Refill derives descriptions and maps without structural
      scheduling. Run preparation uses the run’s actual work estimates
      under normal speed budgets.
4.  1 cache, keyed by structure (part 10): a function value’s entry
    holds its recordings; a forced graph’s plan sits under its graph’s
    structure, inputs keyed by part 10’s read kinds; a lone node is a
    graph of 1. Ordinary code refills as a replay does: a growing value
    read outside a trace ≈ 300·N + 150 + (235 + 130)·N ≈ 665·N a step.
5.  Memory between reads: pending graphs keep leaves alive, not
    intermediates. Node estimates include origins and variable operands,
    output descriptions and argument trees. The memory limit reads bytes
    from descriptions, not a call plan.
6.  Scalar runs: host `⍣` invokes its scalar body through normal
    recorded-call replay (150 + 135·N an iteration); a scalar op outside
    a loop ≈ 685 (accepted: “The bar”, “No cost when unused”).
7.  A new length alone does not produce a compiled version. Counts are
    run-time parameters. A useful recipe that emits different code
    compiles on its first justified use, then reuses that code (part 9).

### Checking

The closing pass counts each stage’s functions (`call_profile`;
`profile` with `defer=`) against these budgets. Over budget → a gap in
that stage, fixed toward its budget, never by a 2nd route.

## The bar

Futhark, Dex and Julia set the standard. A design at least as good would
meet the criteria below. Each one can be checked with
`basedpl.bpltests.time_cases` or a reference test.

<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr>
<th>Criterion</th>
<th>Meaning</th>
<th>Reference point</th>
</tr>
</thead>
<tbody>
<tr>
<td>Gradient cost</td>
<td>The gradient of a scalar function of n variables costs a small
constant multiple of one evaluation, whatever n is.</td>
<td>On ADBench, Futhark’s full Jacobian costs 3.2 to 13.0 times the
objective, and hand-written code 4.4 to 8.6 times
(<code>links/arXiv-2202.10297.md</code>, section 7.1).</td>
</tr>
<tr>
<td>Primal cost</td>
<td>Under <code>∂</code>, the function’s own computation runs on the
same whole-array kernels as without <code>∂</code>, and keeps the fusion
it would get without <code>∂</code>. The derivative adds whole-array
work beside it. Nothing sends the original computation down a per-item
path.</td>
<td>Futhark’s reverse mode re-executes the original statements unchanged
(<code>links/arXiv-2202.10297.md</code>, section 4).</td>
</tr>
<tr>
<td>Bulk preserved</td>
<td>The derivative of a whole-array operation is a whole-array
operation. A map’s derivative is a map. The transpose of indexing is an
accumulation, not a one-hot array or a sequential loop.</td>
<td>Dex paper, sections 5.3 and 7.3. Futhark’s <code>vjpMap</code>,
<code>diffReduce</code> and <code>diffScan</code>.</td>
</tr>
<tr>
<td>Coverage</td>
<td>Primitives in a documented subset, trains, operators with any
operand, branches through Agenda (<code>⍚</code>), and records of
parameters. Recursion is out of scope (part 10).</td>
<td>JAX branches on traced values only through <code>lax.cond</code>,
<code>lax.switch</code> and <code>select</code>.</td>
</tr>
<tr>
<td>One rule set</td>
<td>Each scalar glyph’s partial derivatives are written once and serve
both modes. Each linear operation has one transpose. A user rule
attaches once and works in both modes.</td>
<td>Futhark’s <code>Derivatives.hs</code>, Dex’s
<code>linearizeOp</code>, JAX’s <code>custom_jvp</code>.</td>
</tr>
<tr>
<td>Higher orders</td>
<td><code>∂∂</code> is <code>∂</code> applied to the function
<code>f∂</code>, with no confusion between nesting levels.</td>
<td>ForwardDiff’s tags. JAX nests transformations.</td>
</tr>
<tr>
<td>No cost when unused</td>
<td>Code that isn’t differentiated runs as fast as now, except agreed
exceptions. Agreed: always-lazy evaluation gives every call a node and a
read, so a lone primitive call costs about 1.5 to 2 times today’s eager
call (about 685 instructions plus its work: “Cost model”). Preserve
released ordinary iteration costs; new native loop compilation is
deferred.</td>
<td></td>
</tr>
<tr>
<td>Errors</td>
<td>Construction and host errors stay checked; dynamic graph-numeric
execution also checks data errors, while static graph-numeric execution
follows Measures’ non-raising policy. When and how a checked deferred
error surfaces isn’t promised. Leaving an error guard forces the work
made inside it, so the guard catches its errors. Effects keep their
order among themselves; an effect forces only what it reads.</td>
<td><code>meta/ideas.md</code>, “Block fusion”, names late errors as a
cost of lazy arrays.</td>
</tr>
<tr>
<td>Composes with optimisation</td>
<td>The rewrite rules and the scheduler apply to tangent and transpose
nodes as to any other nodes. Derivative code fuses, windows and stencils
included. The scheduler recomputes a residual that is cheap to compute
again, rather than storing it.</td>
<td>XLA simplifies and fuses the programs that JAX’s transforms
produce.</td>
</tr>
<tr>
<td>Evaluator</td>
<td>The change doesn’t repeat evaluator semantics.</td>
<td></td>
</tr>
<tr>
<td>Build time</td>
<td>After a small edit to one function, the dev build rebuilds about as
fast as now. Functions that every call goes through, such as
<code>Primitive::call_axis</code>, show growth first.</td>
<td>The documented compilation procedure and archived target
baseline.</td>
</tr>
</tbody>
</table>

Gradients over arrays of thousands of values or more, as in neural-net
training and image diffusion, are in scope. `∂` therefore needs reverse
mode, and the gradient-cost criterion is a requirement.

Build-time comparisons use the documented DEV.md compilation commands
with the same host, profile, dependency state and kind of edit. Separate
warm dependency builds from small incremental edits. Historical target
measurements are preserved in the planning archive
`meta/archive/target-build-time-baseline.md`; obsolete function names
and dated reruns are not interface requirements.

## Acceptance

After compile integration, every model in `tests/speed/models.bpl` and
each held-out set (fresh agents using the docs; implementation forks do
not read them) passes under static kinds. Its forward pass and each
scalar loss’s derivative use the shared graph and cache, and ordinary
dense work meets first-principles costs with each gradient at most about
three times its forward pass. Two spellings give the same documented
results and comparable costs. Training iteration reuses its ordinary
recorded body; there is no requirement for a single compact
training-loop node, native loop kernel, compact reverse-loop graph,
checkpoint bound or segmented ragged route. Power and dfn fold/scan AD
may unroll, with graph/saved-value size proportional to actual executed
work. Keep the released language behavior for histories, predicates,
convergence, negative counts, nested arrays, Key/Partition, nested AD,
effects, draws and external conversions. A deferred capability’s former
dedicated requirement must not force its machinery back into the
foundation. Record the scope change rather than silently raising a
budget; a conflict with common released behavior or substantive cost
requires Jeremy’s decision. Under dynamic kinds every model stays
correct and costs remain recorded. The two 4x-aim cases remain a
real-form fused chain and integer date arithmetic. Every speed rule has
a model-independent reason and reference case. Numeric migration
preserves example purpose: retain `+/÷≢`, choose float data, floored
machine results or explicit `ᵣ` case by case, and keep Python/JSON
conversions. Check top-width wrapping, denominator-one R retention,
negative floor division, `÷ₐ`/`÷ᵣ` and identical opaque-input range
policy for forced graphs and recordings. The numeric range policy
relaxes no speed budget. All current-scope Not-list deletions and
DEV.md’s full checks remain future gates. Revised stage 3 writes the
intended pieces, with no separate completeness audit. Compile
integration finds wiring gaps, followed by correctness and cost checks.
Source writing claims none of those results.

## Use

- A coherent milestone names its target parts, removed mechanisms, net
  source lines and unresolved departures.
- Review against the target, not neighbouring code. Record remaining
  departures by part and home; reject a new parallel mechanism rather
  than accommodate it.
- Keep the documented design current. Keep no separate amendment
  document.
