How to propagate a change across the three repositories that hold the Urbit
numerical stack. Read this before syncing /lib/math, /lib/unum, /lib/twoc,
/lib/lagoon, /lib/complex, /lib/fixed, saloon, or maroon.
| repo | holds | path |
|---|---|---|
| urbit/numerics | canonical master — the Hoon libs AND a C jet reference mirror | <fam>/desk/…, <fam>/vere{,64}/… |
| urbit/urbit | the Arvo desk that ships the Hoon | pkg/arvo/lib/*.hoon, pkg/arvo/sur/*.hoon |
| urbit/vere | the runtime that compiles and registers the jets | pkg/noun/jets/…, pkg/noun/build.zig |
numerics is the source of truth. The Hoon in <fam>/desk/lib/*.hoon is the
normative spec; a jet must match its Hoon arm bit-for-bit. Changes flow
numerics → urbit (Hoon) and numerics → vere (jets, applied by hand).
Exception in practice: a fix sometimes lands in a vere PR first (typically refcount-linter or build fixes). When it does, mirror it back to numerics so there is no delta. The invariant is that all three stay in sync, not that edits only ever start in numerics.
Each family directory with jets (lagoon/, libmath/, maroon/, saloon/)
contains:
<fam>/desk/— the Hoon:lib/*.hoon,sur/*.hoon,tests/. This ships to urbit/urbit.<fam>/vere/noun/jets/— the C jet reference mirror:i/*.c,i/*.h,q.h,w.h,<kelvin>/tree.c. This is applied by hand into urbit/vere.<fam>/vere64/…— the post-vere64 twin, where the split still exists.
These jet mirrors are per-family subsets, not full vere trees. libmath/
carries only the math/unum/twoc jets; lagoon/ only lagoon; and so on.
vere develop merged the 64-bit loom (vere64, urbit/vere#970): one source
now builds both looms via -Dvere64, with uniform names (c3_h, u3i_half)
instead of #ifdef. Direct atoms are 31-bit on the 32-bit loom, 63-bit on the
64-bit loom.
numerics still keeps two mirrors per family:
<fam>/vere/ (pre-vere64, 32-bit twin) |
<fam>/vere64/ (post-vere64) |
|
|---|---|---|
| half-word type | c3_w |
c3_h |
u3r_mean form |
variadic: u3r_mean(cor, ax, &a, …, 0) |
braced: u3r_mean(cor, {ax, &a}, …) |
- A change must go to both
vere/andvere64/where the split exists. - Loom-agnostic changes (a
u3_weakretype, a comment/@Refcountannotation, a new arm's logic) are byte-identical in both mirrors. - Loom-specific lines differ only in the two rows above.
lagoon/is being collapsed into a single both-loomlagoon/vere/(mirrors unified vere develop); once merged, treat lagoon as one mirror. Check whether<fam>/vere64/still exists before assuming a split.
numerics carries per-family jet mirrors; the vere branches are full
trees. So a whole-file diff of the shared headers — q.h, w.h,
<kelvin>/tree.c — is dominated by other jet families and is not a real
delta. Do not read those diffs wholesale.
- Use the family's
.cfile as the unit of truth. It is self-contained; verify a sync by byte-identity (diff -q), not by staring at a header diff. - To isolate a real (non-typing) delta, normalize the typedef on both sides
first:
diff <(sed 's/u3_weak/u3_noun/g' A) <(sed 's/u3_weak/u3_noun/g' B). - For
q.h/w.h/tree.c, compare only the family's own lines (grep 'u3qi_<fam>_',grep '<fam>') or diff against the same family's mirror — never against the full vere tree.
The refcount linter (dozreg, urbit/vere#1059)
Jets must pass dozreg's checker with 0 findings, including --strict-weak.
- Any wrapper/helper that can
return u3_none(a jet that punts to Hoon) must be typedu3_weak— in the.cdefinition and theq.h/w.hdeclaration.u3_weakis atypedefofu3_noun(no runtime effect); the type is what the linter reads. - A local holding a possibly-none product (
u3r_at, anotheru3_weakcall) must beu3_weak. - Pointer out-params carry an
@Refcount:annotation, e.g.// @Refcount: fills transferredvalfor a callee that always writes a transferred noun to `*val`; addonc3y`` for a conditional (u3r_cell-style) fill. - Reads that assume a direct atom go through
u3r_cat, notu3x_atom-as-integer. - Run the linter locally before every jet PR; see the vere-side run notes.
- Edit the Hoon in
<fam>/desk/lib/*.hoon(+sur/, +tests/). This is the spec. - Edit the jet reference in
<fam>/vere/and<fam>/vere64/(both looms). Keep the.cbit-exact to the Hoon arm. - Mirror the Hoon to urbit/urbit
pkg/arvo/lib/(+sur/, + tests). - Mirror the jets to urbit/vere
pkg/noun/jets/{i/*.c, q.h, w.h, <kelvin>/tree.c}and add the.ctopkg/noun/build.zigif new. - Verify (all three): build both looms
-Werror; refcount linter 0 under--strict-weak; run the family's desk test suite jetted on a fakezod (tmux driver), matching unjetted Hoon.
- Pick the matching numerics mirror by loom form (braced
u3r_mean→vere64/; variadic →vere/). - The family's
.ccan usually be copied wholesale from the vere branch (numerics = "vere minus the other families" for one family's.c). Forq.h/w.h/<kelvin>/tree.c, merge in only that family's lines — never clobber the other families the numerics header already carries. - Verify the family's
.cis byte-identical to the vere source; PR to numerics.
- Work in worktrees, one per target branch; never disturb scratch branches.
- Worktree builds fail on
zig buildbecausebuild.zigreads.git/logs/HEADand a worktree's.gitis a file. Temporarilymv .git .git-file-bak && ln -s <repo>/.git/worktrees/<name> .git, build, then restore the file before committing. - If a repo is on its default branch, branch first. One focused PR per concern (don't bundle unrelated families or a build fix with a jet fix).
- Registration ships to the current kelvin only (
<kelvin>/tree.c, 135 for the 408k pill); local testing may touch 136/137 but those are not shipped.
Before trusting a "the spec says …" comment, or any C behavior that diverges from
the Hoon, trace it in git. numerics Hoon is ground truth. (Cautionary case: a
truncating C-fmod %mod was once fabricated against the Hoon, which always used
+toi; it was reverted across desk Hoon, both jets, and tests.)
- Per-family details:
<fam>/README.md(e.g.libmath/vere/README.md,libmath/vere64/README.md,lagoon/README.md). - Review/plan state:
WORKPLAN.md; reference PDFs and audits indoc/.