docs: add phase 0+1 design spec
This commit is contained in:
commit
eac7460ff0
1 file changed
+461
@@ -0,0 +1,461 @@
|
|||||||
|
# valw: Phase 0 + 1 design
|
||||||
|
|
||||||
|
Date: 2026-09-29
|
||||||
|
Status: approved in brainstorming, pending written-spec review
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
|
||||||
|
valw is a macOS-style (Cmd+Shift+3/4/5) screenshot tool for niri on NixOS,
|
||||||
|
written in Rust. The full product vision (preview thumbnail, editor, zoom mode,
|
||||||
|
toolbar) is described in the user's original spec
|
||||||
|
(`wayland_screenshot_tool_spec.md`, where the project was called `snapr`).
|
||||||
|
That vision is split into six phases; each phase gets its own
|
||||||
|
design → plan → implementation cycle.
|
||||||
|
|
||||||
|
This document covers **Phase 0 (discovery + scaffolding)** and
|
||||||
|
**Phase 1 (core capture)** only.
|
||||||
|
|
||||||
|
### Goals
|
||||||
|
|
||||||
|
- A dendritic Nix flake that builds valw and provides a devShell.
|
||||||
|
- `valw screen` and `valw region` capture pixel-exact PNGs, save them and copy
|
||||||
|
them to the clipboard.
|
||||||
|
- Errors explain what happened, why, and what to do next, and every run leaves
|
||||||
|
a log good enough to debug from.
|
||||||
|
- `valw doctor` reports what the running compositor supports.
|
||||||
|
|
||||||
|
### Non-goals (later phases)
|
||||||
|
|
||||||
|
- Preview thumbnail, desktop notifications, `--no-preview` (Phase 2)
|
||||||
|
- Window mode, Shift/Alt/Space modifiers, size indicator, fonts (Phase 3)
|
||||||
|
- Zoom mode (Phase 4)
|
||||||
|
- Editor (Phase 5)
|
||||||
|
- Toolbar, drag and drop, home-manager module, README (Phase 6)
|
||||||
|
- `ext-image-copy-capture-v1` backend (not offered by niri 26.04)
|
||||||
|
|
||||||
|
## 2. Verified environment
|
||||||
|
|
||||||
|
Checked on the user's machine on 2026-09-29:
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|---|---|
|
||||||
|
| niri | 26.04 (nixpkgs) |
|
||||||
|
| `zwlr_screencopy_manager_v1` | v3 |
|
||||||
|
| `ext_image_copy_capture_*` | **not offered** |
|
||||||
|
| `zwlr_layer_shell_v1` | v5 |
|
||||||
|
| `wp_fractional_scale_manager_v1` | v1 |
|
||||||
|
| `wp_viewporter` | v1 |
|
||||||
|
| `wp_cursor_shape_manager_v1` | v2 |
|
||||||
|
| `zxdg_output_manager_v1` | v3 |
|
||||||
|
| `wl_output` | v4 |
|
||||||
|
| `wl_seat` / `wl_shm` | v9 / v2 |
|
||||||
|
| `ext_data_control_manager_v1` / `zwlr_data_control_manager_v1` | v1 / v2 |
|
||||||
|
| Outputs | LVDS-1 1366×768 @ scale 1, HDMI-A-1 1920×1080 @ scale 1 |
|
||||||
|
| Shell | Noctalia |
|
||||||
|
| jj | 0.45.1, repo is **non-colocated** (no `.git` at the root) |
|
||||||
|
| direnv | 2.37.1 with nix-direnv (home-manager) |
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Screen capture uses wlr-screencopy only. The `Capturer` trait stays so a
|
||||||
|
second backend can be added later, but only one implementation is written now.
|
||||||
|
- Fractional scaling must be tested by temporarily setting a scale
|
||||||
|
(`niri msg output HDMI-A-1 scale 1.25`).
|
||||||
|
|
||||||
|
## 3. Conventions
|
||||||
|
|
||||||
|
- Code, comments, error messages, logs and docs are in **English**. Comments
|
||||||
|
are plain and short; they explain why, not what.
|
||||||
|
- Version control is jj. The repo is non-colocated, so Nix treats it as a
|
||||||
|
`path:` flake.
|
||||||
|
- Rust toolchain: stable from nixpkgs (no rust-overlay/fenix).
|
||||||
|
- Test runner: `cargo nextest`. Lints: `cargo clippy -- -D warnings`.
|
||||||
|
|
||||||
|
## 4. Repository layout
|
||||||
|
|
||||||
|
```
|
||||||
|
.envrc
|
||||||
|
.gitignore
|
||||||
|
flake.nix # flake-parts; auto-discovers nix/**/*.ulu.nix
|
||||||
|
flake.lock
|
||||||
|
nix/
|
||||||
|
package.ulu.nix # perSystem.packages.default (buildRustPackage)
|
||||||
|
devshell.ulu.nix # perSystem.devShells.default
|
||||||
|
formatter.ulu.nix # nixfmt-tree, same as copland
|
||||||
|
checks.ulu.nix # perSystem.checks: clippy, fmt, nextest, headless sway
|
||||||
|
Cargo.toml
|
||||||
|
Cargo.lock
|
||||||
|
src/
|
||||||
|
main.rs # clap CLI, subcommand dispatch
|
||||||
|
config.rs # config file loading
|
||||||
|
error.rs # user-facing error type and rendering
|
||||||
|
log.rs # tracing setup, per-run log files
|
||||||
|
lock.rs # single-instance lock
|
||||||
|
wayland.rs # SCTK state: registry, outputs, seat, shm
|
||||||
|
capture.rs # Capturer trait + wlr-screencopy implementation
|
||||||
|
niri.rs # niri IPC wrapper (focused output)
|
||||||
|
region.rs # region selection overlay + state machine
|
||||||
|
render.rs # tiny-skia helpers
|
||||||
|
output.rs # PNG saving, filename template, clipboard
|
||||||
|
doctor.rs # `valw doctor`
|
||||||
|
docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
Files stay flat for now; `overlay/` and `editor/` subdirectories appear once
|
||||||
|
Phases 3 to 5 give them enough content.
|
||||||
|
|
||||||
|
### 4.1 Nix
|
||||||
|
|
||||||
|
`flake.nix` follows copland's pattern (flake-parts + automatic discovery), but
|
||||||
|
discovery is limited to `./nix` so `src/` is not walked:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
imports = filter (hasSuffix ".ulu.nix") (listFilesRecursive ./nix);
|
||||||
|
```
|
||||||
|
|
||||||
|
Systems: `x86_64-linux`, `aarch64-linux`.
|
||||||
|
|
||||||
|
`package.ulu.nix`:
|
||||||
|
|
||||||
|
- `pkgs.rustPlatform.buildRustPackage`, `cargoLock.lockFile = ./Cargo.lock`.
|
||||||
|
- `src` is filtered with `lib.cleanSourceWith` to drop `target`, `result`,
|
||||||
|
`.jj` and `.direnv`.
|
||||||
|
- `nativeBuildInputs`: `pkg-config`. `buildInputs`: `wayland`,
|
||||||
|
`libxkbcommon`.
|
||||||
|
- `meta.mainProgram = "valw"`.
|
||||||
|
- `doCheck = false`; tests run through `checks` instead.
|
||||||
|
|
||||||
|
`devshell.ulu.nix`: `rustc`, `cargo`, `clippy`, `rustfmt`, `rust-analyzer`,
|
||||||
|
`cargo-nextest`, `pkg-config`, `wayland`, `libxkbcommon`, `wayland-utils`,
|
||||||
|
`grim`, `imagemagick` (the last three for manual verification). It sets:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
CARGO_TARGET_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/valw/target"
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the repo has no `.git`, Nix copies the whole directory into the store
|
||||||
|
on every evaluation and ignores `.gitignore`. Keeping `target/` out of the tree
|
||||||
|
keeps that copy small.
|
||||||
|
|
||||||
|
`checks.ulu.nix` provides `clippy`, `fmt`, `nextest` and `headless`
|
||||||
|
(section 10.2).
|
||||||
|
|
||||||
|
### 4.2 direnv
|
||||||
|
|
||||||
|
`.envrc`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
watch_file nix/*.ulu.nix
|
||||||
|
use flake
|
||||||
|
```
|
||||||
|
|
||||||
|
nix-direnv only watches `flake.nix` and `flake.lock` by default; the devShell
|
||||||
|
lives in `nix/devshell.ulu.nix`, so it must be watched explicitly.
|
||||||
|
|
||||||
|
### 4.3 .gitignore
|
||||||
|
|
||||||
|
Whitelist style:
|
||||||
|
|
||||||
|
```gitignore
|
||||||
|
/*
|
||||||
|
!/.gitignore
|
||||||
|
!/.envrc
|
||||||
|
!/flake.nix
|
||||||
|
!/flake.lock
|
||||||
|
!/Cargo.toml
|
||||||
|
!/Cargo.lock
|
||||||
|
!/nix/
|
||||||
|
!/src/
|
||||||
|
!/docs/
|
||||||
|
!/README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Any new top-level file must be added here. Otherwise jj silently ignores it,
|
||||||
|
while the `path:` flake still sees it: the build works locally but the commit
|
||||||
|
is incomplete.
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
|
||||||
|
| Purpose | Crate |
|
||||||
|
|---|---|
|
||||||
|
| Wayland client | `smithay-client-toolkit`, `wayland-client`, `wayland-protocols`, `wayland-protocols-wlr` |
|
||||||
|
| Drawing | `tiny-skia` |
|
||||||
|
| PNG | `image` (png feature only) |
|
||||||
|
| Clipboard | `wl-clipboard-rs` |
|
||||||
|
| niri IPC | `niri-ipc` (version pinned to match niri 26.04) |
|
||||||
|
| CLI | `clap` (derive) |
|
||||||
|
| Config | `serde`, `toml`, `serde_ignored` |
|
||||||
|
| Time / filenames | `chrono` |
|
||||||
|
| Errors | `anyhow` |
|
||||||
|
| Logging | `tracing`, `tracing-subscriber`, `tracing-appender` |
|
||||||
|
| Lock | `rustix` (flock) |
|
||||||
|
|
||||||
|
## 6. Capture pipeline
|
||||||
|
|
||||||
|
Every capture command runs these steps in order.
|
||||||
|
|
||||||
|
1. **Log setup** (section 9.2).
|
||||||
|
2. **Lock.** Take a non-blocking `flock` on `$XDG_RUNTIME_DIR/valw.lock`. If it
|
||||||
|
is held, fail with "valw is already running" (exit 1). A second press of the
|
||||||
|
hotkey is ignored rather than queued. The kernel releases the lock if the
|
||||||
|
process dies.
|
||||||
|
3. **Delay.** Sleep for `--delay <secs>` if given.
|
||||||
|
4. **Connect.** Bind the registry with SCTK and collect outputs (name, logical
|
||||||
|
position and size via xdg-output, physical size, scale), seat, shm,
|
||||||
|
screencopy and layer-shell. A missing required global is an error that
|
||||||
|
names the protocol.
|
||||||
|
5. **Capture.** `Capturer::capture(&[Output], cursor: bool) -> Vec<Frame>`
|
||||||
|
issues one screencopy request per output in parallel and waits for every
|
||||||
|
`ready`. A `Frame` holds RGBA8 pixels at physical resolution plus its output
|
||||||
|
name, logical geometry and scale. `XRGB8888`, `ARGB8888`, `XBGR8888` and
|
||||||
|
`ABGR8888` are converted; `y_invert` is honoured; any other format is an
|
||||||
|
error that names the format. `cursor` comes from `--cursor` or
|
||||||
|
`capture.show_cursor`.
|
||||||
|
|
||||||
|
### 6.1 `valw screen`
|
||||||
|
|
||||||
|
- Default: ask niri IPC for the focused output (`FocusedOutput`), capture only
|
||||||
|
that output, save it and copy it to the clipboard.
|
||||||
|
- `--all`: capture every output and save one file per output, with the output
|
||||||
|
name appended to the filename (`... (HDMI-A-1).png`). Only the focused
|
||||||
|
output goes to the clipboard.
|
||||||
|
- If niri IPC is unreachable (another compositor), log a warning and use the
|
||||||
|
first output.
|
||||||
|
|
||||||
|
## 7. `valw region`
|
||||||
|
|
||||||
|
### 7.1 Surfaces
|
||||||
|
|
||||||
|
Capture all outputs first (section 6), then open one layer-shell surface per
|
||||||
|
output:
|
||||||
|
|
||||||
|
- namespace `valw-overlay`, layer `Overlay`, anchored to all four edges,
|
||||||
|
`exclusive_zone = -1`, `keyboard_interactivity = Exclusive`
|
||||||
|
- background: that output's frozen frame, darkened by 40%
|
||||||
|
- the selection area is copied from the undarkened frame and outlined with a
|
||||||
|
1 physical pixel white border
|
||||||
|
- cursor shape: crosshair (`wp_cursor_shape`)
|
||||||
|
|
||||||
|
Surfaces are owned by the process, so the compositor removes them if valw
|
||||||
|
dies. No overlay can outlive the process.
|
||||||
|
|
||||||
|
### 7.2 HiDPI
|
||||||
|
|
||||||
|
- Each surface asks for its preferred scale via `wp_fractional_scale`. The
|
||||||
|
buffer is allocated at physical size, and `wp_viewporter` sets the
|
||||||
|
destination to the logical size. The frozen frame is shown 1:1 with no
|
||||||
|
resampling.
|
||||||
|
- Pointer events arrive in surface-local logical coordinates. They are
|
||||||
|
converted to global logical coordinates by adding the output's logical
|
||||||
|
position; the selection is stored in global logical coordinates.
|
||||||
|
|
||||||
|
### 7.3 Selection state machine
|
||||||
|
|
||||||
|
```
|
||||||
|
Idle --press--> Dragging{start} --release--> Done(rect)
|
||||||
|
^ |
|
||||||
|
| +--release with size < 2 physical px--> Idle
|
||||||
|
+--Esc / right click (any state)--> Cancelled
|
||||||
|
```
|
||||||
|
|
||||||
|
- While `Dragging`, pointer motion updates the rectangle. Redraws are
|
||||||
|
throttled with frame callbacks. The darkened base pixmap is computed once;
|
||||||
|
each redraw copies it and pastes the bright selection on top.
|
||||||
|
- On release, the rectangle is clipped to the output where the drag started
|
||||||
|
(v1 rule: one output per capture).
|
||||||
|
- Cancel closes all surfaces and exits with code 3 without saving anything.
|
||||||
|
|
||||||
|
### 7.4 Logical to physical conversion
|
||||||
|
|
||||||
|
For an output at logical position `(ox, oy)` with scale `s` and a selection
|
||||||
|
`(x, y, w, h)` in global logical coordinates:
|
||||||
|
|
||||||
|
```
|
||||||
|
x0 = floor((x - ox) * s) y0 = floor((y - oy) * s)
|
||||||
|
x1 = ceil((x + w - ox) * s) y1 = ceil((y + h - oy) * s)
|
||||||
|
```
|
||||||
|
|
||||||
|
The result is clamped to the frame bounds and cropped from the in-memory
|
||||||
|
frame. The output always matches the frozen image the user saw, even if the
|
||||||
|
screen changed during selection.
|
||||||
|
|
||||||
|
## 8. Output, config and CLI
|
||||||
|
|
||||||
|
### 8.1 Config
|
||||||
|
|
||||||
|
Path: `$XDG_CONFIG_HOME/valw/config.toml`. Phase 1 reads:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[save]
|
||||||
|
directory = "~/Pictures/Screenshots"
|
||||||
|
filename = "Screenshot %Y-%m-%d at %H.%M.%S.png"
|
||||||
|
copy_to_clipboard = true
|
||||||
|
|
||||||
|
[capture]
|
||||||
|
show_cursor = false
|
||||||
|
```
|
||||||
|
|
||||||
|
- A missing file means defaults (the values above).
|
||||||
|
- Invalid TOML is an error that includes the line number (exit 1).
|
||||||
|
- Unknown keys (e.g. `[preview]`, which later phases read) print a warning
|
||||||
|
through `serde_ignored` and are otherwise ignored.
|
||||||
|
- `~` and `$HOME` in `directory` are expanded. The directory is created if
|
||||||
|
missing.
|
||||||
|
|
||||||
|
### 8.2 Saving
|
||||||
|
|
||||||
|
- The filename is rendered with `chrono` strftime. If the file exists,
|
||||||
|
` (2)`, ` (3)`, … is inserted before the extension.
|
||||||
|
- PNGs are written to a temporary file in the target directory and then
|
||||||
|
renamed, so a partial file never appears.
|
||||||
|
- Each saved path is printed to stdout on its own line.
|
||||||
|
|
||||||
|
### 8.3 Clipboard
|
||||||
|
|
||||||
|
`wl-clipboard-rs` copies the PNG as `image/png` and forks into the background
|
||||||
|
to serve paste requests. The forked child would inherit the lock file
|
||||||
|
descriptor and keep the lock held, so **the lock is released explicitly before
|
||||||
|
copying**. A test covers this.
|
||||||
|
|
||||||
|
### 8.4 CLI
|
||||||
|
|
||||||
|
```
|
||||||
|
valw screen [--all] [COMMON]
|
||||||
|
valw region [COMMON]
|
||||||
|
valw doctor
|
||||||
|
|
||||||
|
COMMON:
|
||||||
|
--clipboard-only copy to clipboard, do not save a file
|
||||||
|
--output <path> write to this path instead of directory + template;
|
||||||
|
"-" writes PNG to stdout (not allowed with --all)
|
||||||
|
--delay <secs> wait before capturing
|
||||||
|
--cursor include the cursor in the capture
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag interactions:
|
||||||
|
|
||||||
|
- `--clipboard-only` conflicts with `--output` and `--all`.
|
||||||
|
- Otherwise the clipboard follows `save.copy_to_clipboard`, whatever the
|
||||||
|
output target (including `--output -`).
|
||||||
|
- `--clipboard-only` copies even if `save.copy_to_clipboard = false`.
|
||||||
|
|
||||||
|
### 8.5 Exit codes
|
||||||
|
|
||||||
|
| Code | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| 0 | success |
|
||||||
|
| 1 | error (missing protocol, lock busy, I/O, …) |
|
||||||
|
| 2 | usage error (clap) |
|
||||||
|
| 3 | cancelled by the user |
|
||||||
|
|
||||||
|
## 9. Errors and diagnostics
|
||||||
|
|
||||||
|
### 9.1 User-facing errors
|
||||||
|
|
||||||
|
Every error shown to the user has three parts:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: could not capture output HDMI-A-1
|
||||||
|
cause: compositor offered only XBGR2101010, which valw can't convert yet
|
||||||
|
hint: run `valw doctor` and include its output in a bug report
|
||||||
|
log: ~/.local/state/valw/logs/2026-09-29T19-40-12.log
|
||||||
|
```
|
||||||
|
|
||||||
|
- `error` is the top-level `anyhow` context; `cause` lines are the rest of
|
||||||
|
the context chain.
|
||||||
|
- `hint` is attached with a small extension trait (`.hint("...")`). Hints are
|
||||||
|
added only where there is a concrete action; no generic "something went
|
||||||
|
wrong" hints.
|
||||||
|
- `log` points at this run's log file.
|
||||||
|
|
||||||
|
### 9.2 Per-run logs
|
||||||
|
|
||||||
|
- `tracing` writes one file per run to `$XDG_STATE_HOME/valw/logs/`; the 10
|
||||||
|
most recent are kept.
|
||||||
|
- Each log starts with an environment summary: valw version, niri version,
|
||||||
|
bound globals and their versions, outputs (name, position, size, scale), and
|
||||||
|
the buffer formats screencopy offered.
|
||||||
|
- The default level is `info`; `VALW_LOG=debug` (EnvFilter syntax) raises it.
|
||||||
|
- stderr gets warnings and errors. Processes spawned by niri log stderr to
|
||||||
|
niri's journal (`journalctl --user -u niri`), but the log file is the
|
||||||
|
primary record.
|
||||||
|
|
||||||
|
### 9.3 Panics
|
||||||
|
|
||||||
|
A panic hook writes the panic and its backtrace to the log and prints the
|
||||||
|
same three-part format to stderr, with "this is a bug in valw" as the hint.
|
||||||
|
|
||||||
|
### 9.4 `valw doctor`
|
||||||
|
|
||||||
|
Prints one `ok` / `warn` / `fail` line per check and exits 1 if any check
|
||||||
|
fails:
|
||||||
|
|
||||||
|
- required globals present with a sufficient version (screencopy, layer-shell,
|
||||||
|
xdg-output, shm, seat), optional ones reported (fractional-scale, viewporter,
|
||||||
|
cursor-shape, data-control)
|
||||||
|
- outputs with position, size and scale
|
||||||
|
- niri IPC reachable, and niri's version compatible with the pinned `niri-ipc`
|
||||||
|
- config file parses
|
||||||
|
- save directory is writable
|
||||||
|
|
||||||
|
Phase 0's discovery report is produced by this command. Later phases add
|
||||||
|
checks.
|
||||||
|
|
||||||
|
Desktop notifications for errors come in Phase 2.
|
||||||
|
|
||||||
|
## 10. Testing
|
||||||
|
|
||||||
|
### 10.1 Unit tests (`cargo nextest`)
|
||||||
|
|
||||||
|
Wayland-independent logic lives in pure functions:
|
||||||
|
|
||||||
|
- logical to physical conversion at scales 1, 1.25 and 1.5, negative output
|
||||||
|
offsets, clamping to output bounds
|
||||||
|
- selection state machine: drag, sub-2px click, Esc, right click, drag that
|
||||||
|
crosses into another output
|
||||||
|
- pixel format conversion for the four formats, `y_invert`, unsupported
|
||||||
|
format error
|
||||||
|
- filename template, collision suffix, `--output -`
|
||||||
|
- config: defaults when missing, unknown key warning, invalid TOML line number
|
||||||
|
- lock: acquire, busy error, forked child does not hold the lock
|
||||||
|
- error rendering: snapshot of the three-part output
|
||||||
|
|
||||||
|
### 10.2 Headless integration test (`nix flake check`)
|
||||||
|
|
||||||
|
A check starts **sway** with `WLR_BACKENDS=headless` and
|
||||||
|
`WLR_RENDERER=pixman` inside the build sandbox, then:
|
||||||
|
|
||||||
|
- runs `valw doctor` and asserts no `fail` lines
|
||||||
|
- runs `valw screen -o -` and asserts the PNG size equals the headless
|
||||||
|
output's size
|
||||||
|
|
||||||
|
This exercises the real screencopy path on every build. It runs on wlroots,
|
||||||
|
not niri. `region` is not covered because it needs synthetic pointer input.
|
||||||
|
|
||||||
|
### 10.3 Manual checks on niri (end of each phase)
|
||||||
|
|
||||||
|
- **Pixel exactness:** capture a region with `valw region` and the same
|
||||||
|
geometry with `grim -g "<geo>"`; `magick compare -metric AE` must report 0.
|
||||||
|
Repeat at scale 1.25 (`niri msg output HDMI-A-1 scale 1.25`) and on both
|
||||||
|
outputs.
|
||||||
|
- **Latency:** the log records time from process start to the first overlay
|
||||||
|
commit; target under 150 ms.
|
||||||
|
- **No stuck overlay:** `kill -9` during selection; the overlay disappears and
|
||||||
|
the next run gets the lock.
|
||||||
|
- **Noctalia:** the overlay covers the bar.
|
||||||
|
|
||||||
|
## 11. Phase 0 exit criteria
|
||||||
|
|
||||||
|
- `nix build`, `nix develop` and direnv work.
|
||||||
|
- `nix build` does not copy `target/` (verified by store path size).
|
||||||
|
- A minimal layer-shell surface opens and closes on niri.
|
||||||
|
- The pinned `niri-ipc` version talks to niri 26.04's socket.
|
||||||
|
- wl-clipboard-rs fork behaviour and its interaction with the lock are
|
||||||
|
confirmed.
|
||||||
|
- `valw doctor` produces its first report.
|
||||||
|
|
||||||
|
## 12. Definition of done (every phase)
|
||||||
|
|
||||||
|
`cargo clippy -- -D warnings`, `cargo fmt --check`, `cargo nextest run` and
|
||||||
|
`nix build` pass, and the manual checks in 10.3 that apply to the phase are
|
||||||
|
done on niri.
|
||||||
Reference in new issue
Block a user