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