| 1 |
# Overnight backlog 4: beating Git on the Linux tree |
| 2 |
|
| 3 |
Loop 3 made oxy usable on the Linux kernel. This loop aims to make it |
| 4 |
faster than Git wherever the model allows it, with the kernel tree as the |
| 5 |
yardstick. Correctness is never traded for speed: every item keeps its |
| 6 |
tests, and the test builds that compare an incremental structure with a |
| 7 |
full computation stay on. |
| 8 |
|
| 9 |
## The yardstick |
| 10 |
|
| 11 |
The Linux tip on Earth (`~claude/bench/linux`, 95 943 files, 1.8 GB): |
| 12 |
`o` is the oxy repository and `g` the Git one. Git is 2.53. |
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
| init and first commit | 38.5 s, 4.4 GB peak RSS | 38.8 s | under Git, under 2 GB | |
| 17 |
| status, clean | 0.41 to 0.43 s | 0.26 to 0.32 s | at or under Git | |
| 18 |
| status, one file edited | 1.26 to 1.39 s | 0.26 to 0.29 s | at or under Git | |
| 19 |
| diff, one file | 1.19 to 1.45 s | 0.16 to 0.25 s | at or under Git | |
| 20 |
| commit, one file | about 3.1 s | 0.45 s | under 0.5 s | |
| 21 |
| status, diff and commit, 1 000 files | 3.9, 2.8 and 4.9 s | 0.41, 0.95 and 0.57 s | at or under Git | |
| 22 |
| log -n 100 | 1.39 s | 0.01 s | under 0.05 s | |
| 23 |
| blame MAINTAINERS | 1.18 s | 0.09 s | at or under Git | |
| 24 |
| checkout to the first state and back | 2.2 to 2.8 s | 0.42 to 0.47 s | at or under Git | |
| 25 |
| fsck | 22.1 s | 13.1 s | at or under Git | |
| 26 |
| gc | 39.7 s | 102.8 s | stays ahead | |
| 27 |
| store after gc | 324 MB | 295 MB | at or under Git | |
| 28 |
|
| 29 |
Where one-file status time goes now (Earth, loaded): |
| 30 |
- index load: 0.16 to 0.28 s |
| 31 |
- walk: 0.45 to 0.7 s |
| 32 |
- manifest check before the snapshot: 0.1 s |
| 33 |
- placeholders for unchanged files: 0.09 s |
| 34 |
- tracked paths: 0.04 s |
| 35 |
|
| 36 |
## Rules for this loop |
| 37 |
|
| 38 |
- **Measure quietly.** Earth runs OxygenFlow jobs, for every push to the |
| 39 |
Oxygen repositories and for metalgear. Measure when `pgrep firecracker` |
| 40 |
finds nothing if you can; otherwise run oxy and Git alternately, three |
| 41 |
times each, and record the load average. Push your commits after a |
| 42 |
measurement, not before, since each push starts jobs. |
| 43 |
- **Build the bench binary** from the synced source: |
| 44 |
`rsync -a --delete --exclude target --exclude .oxy ~/development/oxygen/ claude@earth:bench/oxygen/`, |
| 45 |
then `CARGO_TARGET_DIR=~/bench/idxbin cargo build --release` there. Use |
| 46 |
`~/bench/prof-status.sh`, `~/bench/vs-git.sh` and `~/bench/linux-bench.sh`, |
| 47 |
and extend them rather than writing one-off commands. |
| 48 |
- **Phase timings** (`OXY_TIMINGS=1`) name where time goes. Add phases where |
| 49 |
they are missing before optimising. |
| 50 |
- **An index is derived.** Every new index or cache is keyed by what it |
| 51 |
describes, can be rebuilt from the changes at any time, and has a test |
| 52 |
build check that compares it with a full computation (as the state index, |
| 53 |
tree view and kept file order do). |
| 54 |
- **Formats change freely before 1.0,** but a change format bump follows the |
| 55 |
order in loop 3's backlog (old binary commits it; then the runner and hub |
| 56 |
pins; then `make install`). |
| 57 |
|
| 58 |
## Speed |
| 59 |
|
| 60 |
- [ ] B0 **Baseline.** Update `~/bench/linux-bench.sh` for today's build (the |
| 61 |
state index, the manifest cache, clean and dirty status, diff, commit, |
| 62 |
log, blame, checkout, a local clone, fsck, gc, store size) with three runs |
| 63 |
each and oxy and Git alternating. Record the table in `docs/benchmarks.md`. |
| 64 |
- [ ] S1 **Status and diff at Git's speed** (oxygen#41). In order, measuring |
| 65 |
after each: |
| 66 |
- the manifest as a compact binary file read without a parse per line; |
| 67 |
- the walk: compare each file's stat with the manifest without building |
| 68 |
a path string per entry (sorted walk against a sorted manifest), and |
| 69 |
skip the `.oxyignore` machinery inside tracked directories where it |
| 70 |
cannot change the answer; |
| 71 |
- no placeholder `FileState` for unchanged files (audit every user of |
| 72 |
`SnapshotResult.base_snapshot.files` first); |
| 73 |
- load the state index lazily: memory-map it and decode a file's deferred |
| 74 |
history only when that file is built; |
| 75 |
- one pass for status: the manifest check and the snapshot walk share |
| 76 |
their stats. |
| 77 |
- [ ] S2 **Commit at Git's speed.** A commit extends the base it already |
| 78 |
loaded: write the new state index from the old one plus the change, rather |
| 79 |
than loading history again, and do not render what the snapshot rendered. |
| 80 |
Target: one file under 0.5 s, 1 000 files under 0.6 s. |
| 81 |
- [ ] S3 **The change index: log in milliseconds.** Per state, the changes in |
| 82 |
topological order with their generation numbers and where each change's |
| 83 |
metadata is, built from the parent state's index plus the new change. |
| 84 |
`oxy log -n 100` reads 100 entries. Target: under 0.05 s. |
| 85 |
- [ ] S4 **The node index: blame and file history.** Per node, the changes |
| 86 |
that touch it, so blame and `oxy log FILE` cost that file's history. |
| 87 |
Target: blame MAINTAINERS at or under Git. |
| 88 |
- [ ] S5 **Checkout by comparing indexes.** Compare the two states' tree |
| 89 |
indexes by rendered content hash and write only the files that differ. |
| 90 |
Target: at or under Git. |
| 91 |
- [ ] S6 **fsck at or under Git**: parallel object checking and a cheaper |
| 92 |
state pass. |
| 93 |
- [ ] S7 **The first commit ahead of Git, under 2 GB**: stream reading, |
| 94 |
hashing and storing a batch of files at a time, so the whole tree's text |
| 95 |
is never in memory at once. |
| 96 |
|
| 97 |
## Space |
| 98 |
|
| 99 |
- [ ] P1 **Store at or under Git after gc** (324 MB against 295 MB): measure |
| 100 |
what takes the space, then try a trained zstd dictionary per repository |
| 101 |
and packing similar objects together. |
| 102 |
|
| 103 |
## When a speed item is blocked: the other planned work |
| 104 |
|
| 105 |
Andy's choice (2026-09-30): if the loop is stuck on the speed items (no |
| 106 |
idea left that measures better, or blocked on something only Andy can |
| 107 |
decide), it notes why in the log and takes the next item below instead of |
| 108 |
stopping. Each follows the same rules: an issue, tests, the gate, push, |
| 109 |
deploy where it applies, and a log line. Skip an item that needs Andy (a |
| 110 |
decision, an account, hardware) and say so in the log. |
| 111 |
|
| 112 |
1. **Web workspaces (P5), "VS Code in the hub"**, in parts: |
| 113 |
- [ ] P5a **flow-casfs**: a read-only FUSE mount of a state from its |
| 114 |
content-addressed chunks, in oxygenflow, measured (mount time, first |
| 115 |
read, a full walk of the Linux tip). |
| 116 |
- [ ] P5b **Open in Workspace** on a change or bookmark page of the hub: |
| 117 |
openvscode-server (MIT, unmodified) with Oxygen VCS inside a |
| 118 |
Firecracker microVM per workspace, the state mounted read-only through |
| 119 |
casfs with an ephemeral overlay for edits, and commits going back |
| 120 |
through oxy as the signed-in user. The runner hosts it (the image under |
| 121 |
`~/flow`, a workspace job kind); the hub starts it, proxies it to the |
| 122 |
signed-in user only, and stops it when idle. Boot time measured, aiming |
| 123 |
under 100 ms for the mount. Write the design into the hub and |
| 124 |
oxygenflow specs first. A fork of VS Code only if an extension cannot |
| 125 |
do what is needed, decided with Andy. |
| 126 |
2. - [ ] **oxy on projects coming from Git**: say when a folder holding |
| 127 |
`.git` is skipped (oxygen#43), and honour `.gitignore` or offer to import |
| 128 |
it (oxygen#44). |
| 129 |
3. - [ ] **Device sign-in** for apps and `oh` (oxygenhub#67): approve a code on |
| 130 |
the web with password and 2FA, and the app receives a scoped token. The |
| 131 |
Android client needs it. |
| 132 |
4. - [ ] **An Android build image** for OxygenFlow (oxygenflow#19): JDK, |
| 133 |
Android SDK and NDK, and a Gradle cache warmed offline. |
| 134 |
5. - [ ] **OxygenHub VCS**: declare its authentication provider in the |
| 135 |
manifest (oxygenhub-vscode#4), released as the next patch (see the |
| 136 |
Marketplace notes: never reuse a version). |
| 137 |
6. - [ ] **Import at scale**: finish oxygen#25 (per-commit import cost), then |
| 138 |
backlog 3's H3 (Linux history slices of 2 000, 10 000 and 50 000 |
| 139 |
commits, verified) and H4 (vscode's full history, about 130 000 |
| 140 |
commits, into a scratch repository that is never published). |
| 141 |
7. - [ ] **The hub's open issues**: runner management (oxygenhub#41, backlog |
| 142 |
2's H12 parts), a pinned subrepo left out of a Flow job for lack of |
| 143 |
access (#56), faster CI (#29), required checks over the API (#20). |
| 144 |
8. - [ ] **The hub's plan** (`oxygenhub/docs/plan.md`): notifications for |
| 145 |
review requests, mentions and landings; the organisation webhooks page. |
| 146 |
Email verification and password reset need SMTP on Earth, and passkeys |
| 147 |
are for 1.0: skip both unless Andy says otherwise. |
| 148 |
9. - [ ] **Housekeeping**: tick plan items that are already done (for |
| 149 |
example `oxy seal --proposal` exists), and CHANGELOG files for oxygen, |
| 150 |
oxygenhub and oxygenflow like the extensions' (backlog 2's V2). |
| 151 |
|
| 152 |
## Also |
| 153 |
|
| 154 |
- [ ] R1 **Rebase into the working copy** (oxygen#42, Andy's choice for the |
| 155 |
overnight loop): when `oxy rebase CHANGE --onto TARGET` would be |
| 156 |
conflicted, offer to leave the conflict in the working copy to resolve and |
| 157 |
commit, as `merge` does, rather than refusing. |
| 158 |
- [ ] Z1 **Closing summary.** Re-run the full benchmark and summarise, for |
| 159 |
Andy, where oxy matches or beats Git on the Linux tree, and where it does |
| 160 |
not and why. |
| 161 |
|
| 162 |
## Log |
| 163 |
|
| 164 |
- 2026-09-30 Backlog written from the day's measurements (state index, |
| 165 |
changed-only builds and the manifest cache already in). |