the runtime surface

The pinned ABI

What a Jock program can call: the arms the runtime pins, the traits the prelude declares, and who already implements them. Companion to literals and names, which lists these names without saying what they are.

docs/spec/runtime.md is the authority for everything here. R-9 freezes the ABI through 1.0 with append as the one sanctioned change shape — a new arm enters at the END so every prior arm keeps its index, and a modification in place is not available because it would move an index that emitted code already depends on. R-10: the freeze rests on two pins, and only one is a mug.

The pinned arms

37 rows, projected from runtime.md §4.1. Each names its signature and the corpus vectors that exercise it through Jock source — R-11 pins the pairing behaviourally, so a reorder in either table breaks a vector rather than passing quietly.

R-12 is the one to read twice: nineteen arms return Nat, not @. Nat sits below Atom in the aura order, so a declared @ return would not flow into a Nat annotation and these do.

labelsignaturevectorsnotes
hoon.add sub mul div mod(@, @) -> Nat⟨t-jimp-hoon, t-prec-mul, t-text-op-mixed, t-runs-op, t-diag-crash⟩operator targets (§7)
hoon.dec@ -> Nat⟨t-abi-dec, t-cast-sint-out-deep⟩
hoon.lth lte gth gte(@, @) -> Bool⟨t-runs-op, t-opeq-cmp, t-text-op-char-lt, t-text-op-char-gte⟩ordered comparisons (§7); the prelude Lt/Lte/Gt/Gte impls route here
hoon.cripLC -> Str⟨t-text-chars-2, t-text-chars-3, t-text-chars-bad⟩tape → cord
hoon.tripStr -> LC⟨t-jimp-print, t-spot-print, t-text-glyph⟩cord → tape (bytes); every print row rides it, the %slog clue being a tape
hoon.cat(Str, Str) -> Str⟨t-repl-str, t-text-op-cat, t-text-strif-let⟩byte concat (curated cat-3 wrapper)
hoon.met@ -> Nat⟨t-text-len, t-text-bytes⟩byte length (curated met-3 wrapper); reached through impl Str for String, so Str.len converts and the caller writes hoon.met(self as! @)
hoon.mug* -> Nat⟨t-text-mug, t-abi-mug-canon⟩the collection hash (Map/Set, S10.1). t-abi-mug-canon is the treap-canonicity row: insertion order cannot show through a mug-placed treap
hoon.radd rdiv(Float64, Float64) -> Float64⟨t-text-radd, t-text-rdiv, t-text-radd-op, t-text-rdiv-op⟩~(op rd %n) — the %n rounding mode is pinned in the wrapper, with no mode surface
hoon.rsub rmul(Float64, Float64) -> Float64⟨none — UNPINNED⟩the other half of the same family, and the gap is real: no corpus row multiplies or subtracts two Float64s
hoon.rlth rlte rgth rgte(Float64, Float64) -> Bool⟨t-text-rlth, t-text-rlt-op, t-text-rlte-op, t-text-rgte-op, t-eq-nan-ieee, t-eq-zero-ieee⟩IEEE-correct on NaN and signed zero, which is why Eq(Float64) composes from rlte
hoon.mputforall K V. (Map<K->V>, K, V) -> Map<K->V>⟨t-asx-map-struct, t-text-map-put-ix⟩+put:by wrapper
hoon.mgetforall K V. (Map<K->V>, K) -> V?⟨t-arc-map-bracket, t-arc-map-empty, t-map-dup-last⟩+get:by — the unit product IS T?, by construction
hoon.mhasforall K V. (Map<K->V>, K) -> Bool⟨t-text-mhas⟩+has:by; the in/∈ rows over Map route here
hoon.mdelforall K V. (Map<K->V>, K) -> Map<K->V>⟨t-text-mdel⟩+del:by
hoon.mkeyforall K V. Map<K->V> -> Set<K>⟨t-abi-mkey, t-text-mkey, t-text-mkey-has⟩+key:by — builds through the set engine itself, so canonicality is by construction
hoon.sputforall T. (Set<T>, T) -> Set<T>⟨t-arc-set-ctor, t-asx-set⟩+put:in
hoon.shasforall T. (Set<T>, T) -> Bool⟨t-text-shas, t-text-shas-no, t-text-mkey-has⟩+has:in; the in/∈ rows over Set route here
hoon.sdelforall T. (Set<T>, T) -> Set<T>⟨t-text-sdel⟩+del:in
hoon.mtapforall K V. Map<K->V> -> List<(K, V)>⟨t-map-tap, t-map-tap-method⟩+tap:by
hoon.stapforall T. Set<T> -> List<T>⟨t-set-tap, t-set-tap-method⟩+tap:in — treap order, not sorted order
hoon.lturnforall A B. (List<A>, A -> B) -> List<B>⟨t-list-turn, t-list-turn-lambda, t-list-turn-retype⟩+turn — slams only, so no wrapper (R-14)
hoon.lreelforall A B. (List<A>, (A, B) -> B, B) -> B⟨t-list-reel, t-list-reel-empty⟩+reel — seeds the sample, so the wrapper builds its own gate (R-14)
hoon.lrollforall A B. (List<A>, (A, B) -> B, B) -> B⟨t-list-roll, t-list-roll-empty⟩+roll — same wrapper shape as lreel
hoon.mix con dis(@, @) -> Nat⟨t-bit-mix, t-bit-con, t-bit-dis⟩xor / or / and; the @p differential rides them (examples/patp.jock, lib/urbit.jock)
hoon.rsh lsh end(@, @) -> Nat⟨t-bit-rsh, t-bit-lsh, t-bit-end⟩(bit-count, atom)
hoon.muk(@, @, @) -> Nat⟨t-bit-muk⟩murmur3-32 (syd, len, key) — seed, byte length, key
hoon.mink((*, *), scry) -> Tone⟨t-mink-three-way, t-peek-tone-answer, t-peek-tone-absent, t-peek-tone-null-aura⟩virtualization. peekTone lowers onto it (R-27b) and t-mink-three-way calls it directly. This row read ⟨none — UNPINNED⟩ until 2026-11-05, while R-27a rested a claim on the arm; peekTry reaches mock, not mink, so nothing else did
hoon.mock((*, *), scry) -> Toon⟨t-repl-peektry-some, t-repl-peektry-none⟩(mook (mink …)) — tone → toon; trace frames flatten to List<String>
hoon.ssum sdif spro sfra srem(Int, Int) -> Int⟨t-sint-sum, t-sint-sum-mixed, t-sint-sub, t-sint-mul, t-sint-div, t-sint-rem⟩ZigZag @s over +si verbatim
hoon.sdul(Int, @) -> Nat⟨t-sint-dul⟩curated, not mirrored; includes the exact-multiple edge
hoon.scmp(Int, Int) -> Int⟨t-sint-cmp⟩-1/0/+1 as Int (ZigZag), reached via the Lt impl
hoon.sabsInt -> Nat⟨t-sint-conv⟩total magnitude
hoon.sint@ -> Int⟨t-sint-conv⟩+sun:si — the ruled conversion name
hoon.uintInt -> Nat⟨t-sint-conv, t-sint-uint-neg⟩the inverse: crashes %uint-neg on a negative
hoon.iscell* -> Bool⟨t-abi-iscell-cell, t-abi-iscell-atom⟩Nock 3, the only runtime discrimination Jock has
hoon.jam* -> Nat⟨t-abi-jam⟩the substrate's noun serializer. A kernel's [%file %write path jam] effect (chain.md §6.2) carries a jammed noun and has no other way to make one

The prelude traits

22 traits, read from ++prelude in lib/jock.hoon — the source +ripe actually grafts, not a transcription of it. A trait whose method is spelled add(+) binds an operator; the rest are ordinary methods.

TraitBindersMethodsImplemented for
Str—len(self: Self) -> @ · cat(self: Self, t: String) -> String · bytes(self: Self) -> List<Byte> · chars(self: Self) -> List<UniChar> · starts-with(self: Self, pre: String) -> Bool · ends-with(self: Self, suf: String) -> Bool · contains(self: Self, t: String) -> Bool · find(self: Self, t: String) -> @? · split(self: Self, sep: String) -> List<String> · replace(self: Self, from: String, to: String) -> Self · trim(self: Self) -> SelfString
Kernel<E>load(self: Self, old: *) -> Self · peek(self: Self, p: *) -> * · poke(self: Self, e: *) -> (List<E>, Self)— (nothing in the prelude)
FullKernel<E>load(self: Self, old: *) -> Self · peek(self: Self, p: *) -> * · poke(self: Self, e: *) -> (List<E>, Self) · wish(self: Self, txt: *) -> *— (nothing in the prelude)
Roof—answer(self: Self, r: *, p: *) -> *??— (nothing in the prelude)
Meas—len(self: Self) -> @List<T>
Has<T>has(self: Self, p: T) -> BoolMap<K->V> Set<T>
Idx<I, A>at(self: Self, i: I) -> AList<T> Map<K->V>
Put<I, A>put(self: Self, i: I, v: A) -> SelfList<T> Map<K->V>
Slice<I>slice(self: Self, a: I, b: I) -> SelfList<T>
Find—find(self: Self, sub: Self) -> @?List<T>
Tap<A>tap(self: Self) -> List<A>Map<K->V> Set<T>
Del<I>del(self: Self, i: I) -> SelfMap<K->V> Set<T>
Add—add(+)Float64 Int String
Sub—sub(-)Float64 Int
Mul—mul(*)Float64 Int
Div—div(/)Float64 Int
Lt—lt(<)Byte Float64 Int
Lte—lte(<=)Byte Float64 Int
Gt—gt(>)Byte Float64 Int
Gte—gte(>=)Byte Float64 Int
Eq—eq(==)Float64
Show—show(self: Self) -> StringBool Byte Float64 Int String

3 declare no prelude impl: Kernel FullKernel Roof. Add, Sub and friends are not orphans by accident — the numeric column is a DIRECT arm call that never reaches a trait (R-51), so the prelude binds them only where dispatch is needed, which is Float64, Int, String and Byte.

How an operator resolves

runtime.md R-51. %operator and ordered %compare are KERNEL forms, and mint routes on the left operand's widened bar-type.

left operandresolution
numeric aura (%$, %number, %hex)the pinned kernel column below — a direct arm call, NOT overloadable (the fast path; every compiler-emitted + rides it). BOTH operands must be numeric, %mint-op-aura
%realthe ops registry → the prelude's Add(Float64) and friends, r* arms underneath
nominal head (struct/union/class/data) or NON-numeric aura (%chars, %logical, …)the ops registry → trait-method dispatch (+mmet: class-inherent, impl-dictionary, generic-head). An impl gives the operator meaning; its absence refuses %mint-op-impl
bounded %varthe same synthesized method call; the $bnd dictionary route resolves it

Jet status (R-5’s sixth field)

Not per arm, and it cannot be. Every arm above lives in hoon, the one library that is hot by construction: +hoonlib builds it by evaluating in-process, so its arms are the substrate’s own kernel gates and the NockVM hot state matches them by formula identity. What varies is whether anything registers, and that is a property of the artifact you ship, not of the arm you call.

shipping shapegatethe ABI coreeffectivepinned by
kernel%livehoon is %hothott-jres-bare
sealed%deadhoon is %hotcoldt-jres-sealed

So the same call is jetted in a kernel or value artifact and interpreted in a sealed one — %fast registration fires only while a trap evaluates, and a sealed artifact is a constant trap. This is the sealed-kernel finding stated as a predicate; tools/jets.sh answers it per program, and jockc.sh jets is the same lane.

Call sites are attributed individually — hoon.add(20, 22) reports hoon/add/%hot at 1:1 ⟨t-jres-site-hot⟩ — so the question "will the code I wrote hit jets in the artifact I am about to ship" has a build-time answer rather than a deployment surprise.

What this page does not yet carry

The per-arm record format (R-5) asks for six fields — label, formula hash, mask, signature, vectors, jet status. The §4.1 table carries three of them: label, signature and vectors. Its fourth column, notes, is not an R-5 field, and this page counted it as one and claimed four of six until 2026-10-27. Jet status is the section above, which makes it four for real.

The two that remain are formula hash and mask, and neither is withheld for want of a table. A formula hash needs the R-6 hash-diff protocol to pin a hash per arm — tools/jets-lane.sh diffs the stdlib’s hashes in bulk, which catches drift without ever naming which arm moved. The %wild mask needs the $sock cape, whose emission is the paused half of UIP-0122: the molds and the builder exist and pass, and the %hintd emission waits on runtime co-design. Both are tool work, not documentation work, and this page will carry them when the tools answer.