1# Library regression lanes
2
3## The CI gate
4
5`python3 tools/ci.py` gates `main` (or `--rev REV`, or `--working-copy` for this
6checkout's `@`) in the jj workspace `workspaces/ci`: it points that
7workspace's own commit, a child of `main`, at the revision's files, so other
8checkouts' edits in progress never reach the result, and a working copy is
9frozen as it was when the run started. Its build cache is
10`workspaces/ci/target`, apart from every agent's `target/`. Runs wait for one
11another, and the exit status is the result.
12
13| Lane | Runs |
14| --- | --- |
15| `fmt` | `cargo fmt --all --check` |
16| `clippy` | Clippy `-D warnings` on the workspace (all targets and features), and `snowbound` without default features |
17| `test` | builds every test target, runs the executables four at a time (`--test-jobs`) from their package folders, slowest last time first, and the doctests |
18| `python` | builds the examples the suite runs, then `tools/test_*.py` under `uv` with Pillow and pdfplumber |
19| `windows-x86_64` | `platform/windows/cargo.sh` build of `snowbound` (nightly's win7 target) |
20| `windows-aarch64`, `linux-*` | Clippy `-D warnings` on what ships (libraries and binaries but `mobile`), then the `snowbound` build; Linux through `platform/linux/cargo.sh`, which links with zig against glibc 2.17 |
21| `ios` | `xcodebuild` of the simulator app, unsigned |
22| `web` | Clippy `-D warnings` on `snowbound` for `wasm32-unknown-unknown`, SQLite built by nixpkgs' clang; `release_web.py` links, optimizes and deploys the static folder |
23| `web-js` | Node boundary tests for Live Share browser sockets and storage |
24| `macos-10.6` | `platform/snow-leopard/cargo.sh` build of `snowbound`; skipped, saying why, without the SDK or nightly `rust-src` |
25
26```sh
27python3 tools/ci.py # every lane, on main
28python3 tools/ci.py --working-copy --changed # the lanes and test packages @'s changes from main reach
29python3 tools/ci.py --rev xyz --lanes test windows # `windows` names both windows-* lanes
30```
31
32Lanes run four at a time (`--jobs`), each under its own time limit
33(`--timeout MINUTES` overrides them all). The table it prints names each
34failure's first errors with their files and lines, to tell whose edit broke
35it; `workspaces/ci/target/ci/runs/TIME/` keeps every lane's log and
36`summary.json` (status, seconds, errors with files, each test executable's
37time), for the last 20 runs. After a run over `--budget` (40 GB), it deletes
38the build units this run didn't use, least recently used first, which keeps
39`deps/` small for the font tests that scan it. Windows needs llvm-mingw from
40`platform/windows/toolchain.sh` in the main checkout's `target/windows`, or
41`LLVM_MINGW`; Linux needs `zig`. `release.py` runs the gate on the commit it
42publishes.
43
44The workspace is made on first use; to drop it, `jj workspace forget ci` and
45delete `workspaces/ci`.
46
47## Public fixtures
48
49From a checkout with Rust and [uv](https://docs.astral.sh/uv/), which provides
50Python 3.12 with Pillow and pdfplumber without installing anything system-wide:
51
52```sh
53uv run --no-project --python 3.12 --with pillow --with pdfplumber \
54 python tools/check_public.py /absolute/path/to/new-results
55```
56
57The command runs formatting, all workspace features/targets, doctests, Clippy,
58diagnostic/example builds, and Python regression tests. Each stage has its own
59log; `results.json` records executed commands, elapsed times and exit statuses.
60Only a completed run receives `status: passed`. Existing result directories are
61refused. This lane clears inherited `ONESTORE_*` and `SNOWBOUND_*` overrides and
62uses this checkout's binaries so personal captures or external exporters cannot
63silently replace public inputs.
64
65Repeat the same command in a fresh checkout containing only versioned files to
66verify clean-checkout compatibility. Fixture symlinks must remain symlinks; their
67targets are versioned in this repository. Neither `corpus/private`, ignored
68`evidence`, existing `target` outputs, credentials nor running virtual machines
69are required. Cargo's normal dependency cache may be reused.
70
71For the Python tests alone, end the same `uv run` with
72`python -m unittest discover -s tools -p 'test_*.py'`. Without pdfplumber they
73skip the PDF oracles (`test_pdf_format`, part of `test_document_oracle`) and
74say so; `check_public.py` refuses to start without it.
75
76Rust explicitly reports ignored lab tests and fixture generators. Those cases
77are **not** part of a successful public run. The Python suite tests native/lab
78harness logic using retained synthetic captures and mocks; it does not claim a
79new execution of OneNote or a real server interruption.
80
81## Sweeps and soak
82
83Seeded sweeps (random edit walks, multi-client schedules, differentials across
84corpus sections) run a smoke slice by default. `SNOWBOUND_SWEEP=0` runs them in
85full with the seeds as written; any other number shifts every sweep's seeds, and
86a failure replays under the same value.
87
88`tools/soak.sh [seconds per fuzz target]` soaks a dedicated machine until
89stopped: each round runs the full sweeps under a random shift, in release with
90debug assertions, then every `fuzz/` target for the time limit. A failing round
91leaves `soak/*.log` ending in the command that replays it; crash inputs stay in
92`fuzz/artifacts/<target>/`. On a Linux VM, install rustup's stable and nightly
93toolchains, `cargo install cargo-fuzz`, and `build-essential pkg-config
94libfontconfig-dev`; copy the checkout and run the script under `tmux`.
95
96## Motion capture
97
98`SNOWBOUND_FRAMES=DIRECTORY` writes every frame the app draws to
99`DIRECTORY/MILLISECONDS.png`, timed from when the window opened. With a
100hidden-window replay (`SNOWBOUND_REPLAY` plus `--screenshot`, see
101`tools/canvas/README.md`) this records an animation at its real pace without
102touching the screen: waits tick at 60 Hz, and the app draws as fast as it can
103while it animates. Point it at a copy of a notebook, end the script with a
104short `wait` so the last frames finish writing, and assemble strips or GIFs
105with `ffmpeg`.
106
107## Private and native verification
108
109Private notebooks stay outside versioned fixtures. Materialize a copy before
110editing and retain source hashes; never point an authoring harness at an original
111notebook. Live acceptance uses explicitly owned disposable targets and preserves
112the run's commands, inputs, outputs and teardown evidence.
113
114| Boundary | Entry point | Acceptance evidence |
115| --- | --- | --- |
116| Native authoring and cold reopen | `native_runner.py --help` | Independent OneNote capture; exact expected page count when known; owned clone teardown |
117| Password-protected sections | `native_protected.py NOTEBOOK PASSWORDS OUTPUT [SECTION]` | A fresh clone cold-opens Snowbound's protected sections, unlocks each in OneNote's dialog, types into one through COM and reads every page with the notebook it leaves (`corpus/protected-sections`) |
118| Mixed native/Rust/offline writers | `native_collaboration.py --help` | Recorded intents, durable receipts, independent server state and cold native comparison |
119| SMB directory pagination | `test_smb_directory.py --help` | Caller-owned Linux VM, native filesystem oracle, interrupted-page rejection |
120| SMB publication and payload interruptions | Ignored tests in `notebook` (feature `smb`) | Explicit `ONESTORE_SMB_*` lab inputs, retained protocol traces and independent recovery checks |
121| Application session on a share | `session_acceptance.py --help` | Page saves through `notebook::session` on the mounted Samba share, relaunch between launches, cold native reopen of the edited pages, owned VM and clone teardown |
122
123To compare an additional **already captured** notebook hierarchy without running
124OneNote:
125
126```sh
127PYTHONPATH=tools ONESTORE_NOTEBOOK_NATIVE=/absolute/path/to/capture \
128 python3 -m unittest test_notebook_discovery
129```
130
131The capture contains `notebook/` and `read/hierarchy.xml`. This comparison is a
132separate private lane and must retain its own log. A missing capture, unavailable
133lab, compilation-only iOS result or ignored test never establishes live native
134compatibility. Process exit, server/VM interruption and physical storage loss
135remain distinct fault models.