From 9ff1297471a483bad1b2b38ccb1e42614f60fc5e Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 17 Sep 2026 14:29:01 +0200 Subject: 0.0.12: the changelog, the checklist and the status line --- CHANGELOG.md | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 11 ++++--- docs/gui-checklist.md | 7 +++++ 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4dd69..ccb2a62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,85 @@ ## Unreleased +## 0.0.12 — 2026-09-17 + +Published, and reviewed. The repository is at +`https://git.labunix.xyz/krino.git` and the module path is +`git.labunix.xyz/krino`, so `go install git.labunix.xyz/krino/cmd/krino@latest` +works from this release on. Then five reviews — security, correctness, +determinism, performance and the window — found four high-severity defects +and a number of smaller ones; what follows is what they found and what was +done about it, each with the measurement or the test that settles it. + +**Files that could have been lost** + +- A symlink planted in the directory being sorted, named after a rule's + destination, redirected a move or a copy out of the tree — and under + `(on-conflict overwrite)` trashed a file *outside* it — while the plan the + user approved showed only the in-tree text. A step whose destination + passes through a symlink at or below the sorted directory now fails. A + destination the configuration itself names outside that directory, `~/docs` + on another disk say, is the user's own arrangement and is followed as + before. +- A file trashed to make room was logged only when the step that needed its + name had finished — for a copy, the whole data transfer later. Killed in + that window, krino reported "nothing applied" while the user's file sat in + the Trash. It is now logged the moment it is trashed, and a displace that + cannot be logged fails the step. +- A chain ending in `(delete permanent)` never gave back the file it had + displaced: the walk stopped at the delete, so the user's only copy stayed + in the Trash and `krino log` called the run undone. The displaced file is + offered as its own entry in the undo plan. +- A `(duplicate)` test written in an `(exclude ...)` bought no protection at + all: a later rule permanently deleted every copy. Duplicate scopes are now + collected from excludes as well as rules. +- The window could release the directory lock and close the log under a + running Apply — by saving rules, saving settings, adding a directory or + closing the window — so a file moved that no `krino undo` could see. The + tabs refuse to close while their apply is in flight. +- Deleting one of two forms written on the same line deleted both, and + clearing a setting took whatever else shared its line — including the + closing paren of `(defaults ...)`, which broke the file the Settings + window had just written. Both take a whole line only when the line holds + nothing else. + +**The directory lock is now the kernel's.** `flock(2)` on the lock file +replaces "write my pid, and judge whether the pid in the file is still +alive". A crashed run's lock is released by the kernel, so there is no stale +lock to detect and no takeover to race over: two runs could previously both +decide a lock was stale and both take it. A pid reused after a crash no +longer wedges a directory for ever, and a run that waits for a lock says so +before it waits. + +**Faster, measured on invented corpora** + +- A content class is worked out once instead of once per copy: 2000 + identical files, 1.89s → 0.58s, with byte-identical plans. +- The search for a free `stem_N` continues instead of starting again at + `_1`: 1500 files renamed onto one name, 2.63s → 0.05s. +- A file's name is folded once rather than once per name test: 4000 names + with diacritics over twelve rules, 0.33s → 0.13s. +- `max-read` now bounds what a file *becomes*, not only what is read, and + large extractions are rationed: twelve 40 MB files, peak memory 3294 MB → + 728 MB. Small files are untouched. + +**Other** + +- `{now:FMT}` is the start of the run, as the spec always said, rather than + the moment each directory happened to be planned — so a long review cannot + split one run's output across two folders, or two dates. +- The Settings window's sort order, column toggles and preview height were + read but never connected to anything; they work now, and changing a + setting no longer discards the divider positions. +- "Keep this copy, replace the other" refuses a second file aimed at the + same place, which the engine has always refused to plan. +- `About`, at the foot of Settings: the version, the licence, the author and + the repository. +- The Plan tab's toolbar reads in the order the work happens, and `Scan` + carries the theme's accent. +- Comments that cited the development process rather than the code are + rewritten; the README is a third shorter, with a screenshot. + ## 0.0.11 — 2026-09-17 The window, worked over from a fortnight's worth of using it: the plan reads diff --git a/README.md b/README.md index 8b4b526..ef07898 100644 --- a/README.md +++ b/README.md @@ -192,16 +192,15 @@ sudo apt install libgtk-4-dev libgirepository1.0-dev # Debian, Devuan ## Status -**0.0.11.** +**0.0.12.** ``` -git clone https://git.labunix.xyz/krino.git -go install git.labunix.xyz/krino/cmd/krino@latest # from 0.0.12 on +go install git.labunix.xyz/krino/cmd/krino@latest +git clone https://git.labunix.xyz/krino.git # or build from a clone ``` -`go install` needs a release carrying the module path below, so it works -from the next tag; until then, build from a clone. The shape of -`krino -n --json` is unstable before 1.0 — don't script against it yet. +The shape of `krino -n --json` is unstable before 1.0 — don't script +against it yet. Builds and tests are run on Linux, FreeBSD and OpenBSD. The release tarballs hold the cross-compiled command line only; the window is built on diff --git a/docs/gui-checklist.md b/docs/gui-checklist.md index d81e57c..2e8152b 100644 --- a/docs/gui-checklist.md +++ b/docs/gui-checklist.md @@ -157,3 +157,10 @@ A dialog is its own window: take it by its own id, not the main window's. filter, the path on disk, Scan - and Scan carries the theme's accent. 51. Settings ends with About: the program and the version it was built as, the licence, and the contact address, all selectable. +52. Pressing Save in the Rules tab, opening Settings, or closing the window + while an apply is running: refused, with the reason, and the apply + finishes. The other tabs and Settings are greyed out for the duration. +53. Two duplicates of one filed file, "keep this copy" chosen for both: the + second is refused, naming the file that already has the place. +54. Every control in Settings takes effect as it is changed, and changing + one does not move the dividers back to their defaults. -- cgit v1.3