From b73f2155a7c87cd2ab6fdcdc49f76fc134e722c3 Mon Sep 17 00:00:00 2001 From: Sigilante Date: Sat, 12 Sep 2026 14:15:47 -1000 Subject: [PATCH] docs: agent merge guide (numerics -> urbit/urbit + urbit/vere) A practical reference for propagating a numeric-stack change across the three repos: repo roles (numerics is master), the per-family/per-loom mirror layout, the vere64 vere/ vs vere64/ split, why cross-repo header diffs are noisy (per-family mirrors vs full vere trees -> trust the .c by byte-identity), the refcount-linter bar (u3_weak / @Refcount, 0 findings strict-weak), the forward and back-port sync workflows, worktree-build mechanics, and the "trace provenance, Hoon is ground truth" rule. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Rqqco86RgnvtiC4axw8Px3 --- AGENTS.md | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f626ead --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,138 @@ +# Agent merge guide: numerics → urbit/urbit and urbit/vere + +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. + +## The three repos and their roles + +| repo | holds | path | +| --- | --- | --- | +| **urbit/numerics** | **canonical master** — the Hoon libs AND a C jet reference mirror | `/desk/…`, `/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 `/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. + +## numerics layout (per family, per loom) + +Each family directory with jets (`lagoon/`, `libmath/`, `maroon/`, `saloon/`) +contains: + +- `/desk/` — the Hoon: `lib/*.hoon`, `sur/*.hoon`, `tests/`. This ships to + urbit/urbit. +- `/vere/noun/jets/` — the C jet **reference mirror**: `i/*.c`, `i/*.h`, + `q.h`, `w.h`, `/tree.c`. This is applied by hand into urbit/vere. +- `/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. + +## The vere64 split (read this before editing any jet) + +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: + +| | `/vere/` (pre-vere64, 32-bit twin) | `/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/` and `vere64/` where the split exists. +- **Loom-agnostic** changes (a `u3_weak` retype, a comment/`@Refcount` + annotation, 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-loom `lagoon/vere/` (mirrors + unified vere develop); once merged, treat lagoon as one mirror. Check whether + `/vere64/` still exists before assuming a split. + +## Cross-repo diffs are noisy — trust the `.c`, not the headers + +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`, +`/tree.c` — is dominated by **other jet families** and is not a real +delta. Do not read those diffs wholesale. + +- **Use the family's `.c` file 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__'`, `grep ''`) 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 + typed **`u3_weak`** — in the `.c` definition **and** the `q.h`/`w.h` + declaration. `u3_weak` is a `typedef` of `u3_noun` (no runtime effect); the + type is what the linter reads. +- A local holding a possibly-none product (`u3r_at`, another `u3_weak` call) must + be `u3_weak`. +- Pointer out-params carry an `@Refcount:` annotation, e.g. + `// @Refcount: fills transferred `val`` for a callee that always writes a + transferred noun to `*val`; add `` on `c3y` `` for a conditional (u3r_cell-style) + fill. +- Reads that assume a direct atom go through `u3r_cat`, not `u3x_atom`-as-integer. +- Run the linter locally before every jet PR; see the vere-side run notes. + +## Standard sync workflow (change originates in numerics) + +1. Edit the Hoon in `/desk/lib/*.hoon` (+ `sur/`, + `tests/`). This is the + spec. +2. Edit the jet reference in `/vere/` **and** `/vere64/` (both looms). + Keep the `.c` bit-exact to the Hoon arm. +3. Mirror the Hoon to **urbit/urbit** `pkg/arvo/lib/` (+ `sur/`, + tests). +4. Mirror the jets to **urbit/vere** `pkg/noun/jets/{i/*.c, q.h, w.h, + /tree.c}` and add the `.c` to `pkg/noun/build.zig` if new. +5. 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. + +## Back-port workflow (fix landed in a vere PR first) + +1. Pick the matching numerics mirror by loom form (braced `u3r_mean` → `vere64/`; + variadic → `vere/`). +2. The family's `.c` can usually be **copied wholesale** from the vere branch + (numerics = "vere minus the other families" for one family's `.c`). For + `q.h`/`w.h`/`/tree.c`, merge in **only that family's lines** — never + clobber the other families the numerics header already carries. +3. Verify the family's `.c` is byte-identical to the vere source; PR to numerics. + +## Practical mechanics + +- Work in **worktrees**, one per target branch; never disturb scratch branches. +- **Worktree builds** fail on `zig build` because `build.zig` reads + `.git/logs/HEAD` and a worktree's `.git` is a file. Temporarily + `mv .git .git-file-bak && ln -s /.git/worktrees/ .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 (`/tree.c`, 135 for + the 408k pill); local testing may touch 136/137 but those are not shipped. + +## Provenance rule + +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.) + +## Pointers + +- Per-family details: `/README.md` (e.g. `libmath/vere/README.md`, + `libmath/vere64/README.md`, `lagoon/README.md`). +- Review/plan state: `WORKPLAN.md`; reference PDFs and audits in `doc/`.