aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 21:47:06 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 21:47:06 +0200
commit79c746ceac46188deababcf5453c7ca37e173a6a (patch)
treeaaa1df06a356c4d09505b3d9643af67450f85703
parent49ea29a66254cc23265025a912a73c3e5b0c6ae5 (diff)
downloadkrino-79c746ceac46188deababcf5453c7ca37e173a6a.tar.gz
krino-79c746ceac46188deababcf5453c7ca37e173a6a.zip
plan 9: docs and changelog describe what 0.0.7 does
-rw-r--r--CHANGELOG.md86
-rw-r--r--README.md5
-rw-r--r--docs/design.md38
-rw-r--r--man/krino.119
4 files changed, 103 insertions, 45 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0296f8..f34c309 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,39 +4,65 @@
## 0.0.7 — 2026-09-14
-Security and correctness hardening, from a threat model (spec §15.1) and the
-tests that hold it.
+Security and correctness hardening, from a threat model (spec §15.1), an
+independent review, and the tests that hold both.
-- File names, paths and tool messages can no longer control the terminal:
- control characters, bidirectional controls and invalid UTF-8 are printed
- as escapes (`\x1b`, `\u202e`) everywhere krino prints them.
-- A placeholder can no longer rename a file to "", "." or "..", or add a
- ".." to a destination; such a step is skipped with a reason.
+Hostile names and contents
+- File names, paths, tool messages, errors, explain traces, JSON plans and
+ log fields can no longer control or reorder the terminal: control
+ characters, every bidirectional control, line and paragraph separators
+ and invalid UTF-8 are printed as escapes (`\x1b`, `\u202e`).
+- A placeholder can no longer take a destination outside the directory the
+ rule names: a capture of "..", "~" or nothing is refused with a reason,
+ and so is a rename to "", "." or "..".
- A file swapped between planning and applying - for a symlink, or for
- another file with the same size and modification time - is not acted on.
-- Undo refuses a trash entry name that is not a plain name inside the
- Trash, and a trashinfo whose path is not absolute.
+ another file with the same size and modification time - is not acted on;
+ a step that had to take a free name stops the rest of its chain.
+- `on-conflict overwrite` never trashes a directory, or another file of the
+ same plan; it skips or takes a free name instead.
+- A content exclude holds when its content test cannot read the file
+ (fails closed), so a protected file krino could not read is left alone.
+- Extraction tools are only found through absolute PATH entries.
+
+Apply and undo
+- Every step is logged as soon as it has run; SIGHUP is handled like
+ SIGTERM. A run killed mid-chain can be undone as far as it got.
- Undo reverses a file its run renamed and then moved, moved twice, or
- moved and then sent to the Trash. Before, undo judged the first step
- against the disk as it is now, found its destination empty (the later
- step had moved the file on) and refused the whole file as "missing". Found
- by the new generated apply-and-undo test.
-- Folding maps the capital sharp s (ẞ) to "SS", as the other capitals
- already were: "STRAẞE" in a document now matches the keyword "straße".
-- Folding replaces invalid UTF-8 with U+FFFD first, so a stray invalid byte
- no longer stops the letter after it from losing its accent.
-- `krino new` refuses a directory whose path is not valid UTF-8 with that
- reason, instead of reporting a broken template.
-- The keyword cache is also discarded when normalisation changes, so answers
- cached before these fixes are recomputed once.
-- Builds need Go 1.25 or newer and use the Go 1.26.8 toolchain, which an
- older `go` downloads itself. `golang.org/x/text` is updated to v0.41.0.
- This fixes an infinite loop a crafted file's text could cause
- (GO-2026-5970) and, through the toolchain, the XML nesting guard
- (GO-2026-6088) and the `os/exec` and `os` fixes govulncheck reported.
-- Tests: fuzz targets for every decoder of outside data (`make fuzz`), a
- generated apply-and-undo round trip (`KRINO_PROPERTY_RUNS`),
- enum-completeness tests, `make race` and `make vulncheck`.
+ moved and then trashed (it refused them before).
+- Undo restores a trash entry only while it is still the file the run put
+ there, re-checks every file just before acting, keeps same-named files
+ of two directories apart, and leaves no directory behind.
+- An undo that stopped part way can be finished by undoing the run again;
+ plain `krino undo` after an undo does that.
+- The log restores a missing final newline, so a crash mid-line no longer
+ breaks the next run's undo.
+- Trash entries skip names left orphaned in the Trash, and long names are
+ shortened to fit.
+
+Matching and the keyword cache
+- Folding maps the capital sharp s (ẞ) to "SS", and replaces invalid UTF-8
+ first so a stray byte no longer stops the next letter losing its accent.
+- The cache keys on the extension too (it picks the extractor), and is
+ discarded when max-read, the Go release or the Unicode tables change;
+ removed keywords leave it, and an unused cache is removed.
+
+Command line
+- Undo's per-file review behaves like review's (`w` leaves the rest
+ unreviewed, choices echoed).
+- `w` stops krino even when applying fails.
+- `--min-age` is refused by commands it does not apply to, and an empty
+ value is an error; a file dated in the future counts as brand new.
+- `krino new` refuses a path that is not valid UTF-8, with that reason.
+- A rule name cannot start with "(".
+- Builds need Go 1.25 or newer and use the Go 1.26.8 toolchain;
+ `golang.org/x/text` is v0.41.0 (GO-2026-5970, and through the toolchain
+ GO-2026-6088 and the `os/exec` and `os` fixes).
+
+Tests
+- Fuzz targets for every decoder of outside data (`make fuzz`), a generated
+ apply-and-undo round trip comparing files and directories
+ (`KRINO_PROPERTY_RUNS`), enum-completeness tests, `make race`,
+ `make vulncheck`, and a dependency gate that checks modules.
## 0.0.6 — 2026-09-14
diff --git a/README.md b/README.md
index b62bcfe..64c8a72 100644
--- a/README.md
+++ b/README.md
@@ -215,8 +215,9 @@ rule content only when a private pattern list is configured with
`krino log`. A reversal that the world has moved on since (the target is
gone, changed, or the original path is occupied again) is refused for
that file rather than guessed at.
-- `-n` prints the plan and changes nothing, ever — no config is written, no
- file is touched, nothing is logged.
+- `-n` prints the plan and changes none of your files — no config is
+ written, nothing is moved, nothing is logged. It may refresh the keyword
+ cache in `~/.cache/krino`, which only makes the next run faster.
## Status
diff --git a/docs/design.md b/docs/design.md
index 9d7abb5..5b70183 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -678,7 +678,7 @@ krino log [-n N] list recent runs
krino undo [RUN] reverse a run (default: the last one)
-y apply without asking
--n dry run: show the plan, change nothing
+-n dry run: show the plan, change no file (the keyword cache may be refreshed)
-v also list unmatched, ignored and busy files; full match reasons
--json with -n: the plan as JSON (format unstable before 1.0)
-c FILE use FILE instead of ~/.config/krino/krino.conf
@@ -847,18 +847,31 @@ survives their corruption — a truncated line, a damaged cache, a
hand-edited trashinfo — but does not defend against another local process
with write access to them, which could already do anything krino can.
-What that means, and the tests that hold it (plan 8):
+What that means, and the tests that hold it (plans 8 and 9):
-- A name never controls the terminal. Every name, path, reason and warning
- is printed through `display`, which shows C0 controls, DEL, C1 controls,
- Unicode bidirectional embeddings, overrides and isolates, and invalid
- UTF-8 as escapes (`\x1b`, `\u202e`).
-- A name never redirects a step. A rename whose placeholders produce "",
- "." or "..", and a destination whose placeholders add a ".." segment, are
- skipped with a reason.
+- A name never controls the terminal. Everything printed from a name, path,
+ reason, warning, error, explain trace, JSON plan or log field is escaped:
+ C0 controls, DEL, C1 controls, every Unicode bidirectional control
+ (embeddings, overrides, isolates and the marks), line and paragraph
+ separators, and invalid UTF-8 (`\x1b`, `\u202e`). All of stderr goes
+ through the same escaping. Invisible format characters that are not
+ controls are printed as they are (known limitation).
+- A name never redirects a step. A destination with a placeholder must
+ resolve at or under the directory its text names before the first
+ placeholder, so a capture of "..", "~" or nothing cannot move it
+ elsewhere; a rename whose placeholders produce "", "." or ".." is skipped
+ with a reason.
- A file is acted on only while it is still the file that was planned:
same size and modification time, still a regular file (not a symlink put
- in its place), and, at its planned path, the same inode.
+ in its place), and, at its planned path, the same inode. A step that had
+ to land at a free name stops the rest of its chain. Overwrite trashes only
+ a regular file, never a directory or another file of the same plan.
+- Every step is logged as soon as it has run, so a run killed mid-chain can
+ be undone as far as it got.
+- Undo restores a trash entry only while it is still the file the run put
+ there (size, mtime, recorded path), re-checks every file at execution
+ time, and can finish an undo that stopped part way.
+- An exclude whose content test cannot read the file holds (fails closed).
- A file reaches an external tool only as an absolute path, so a name
starting with `-` is never read as an option.
- A trash entry name from the log must be a plain name inside the Trash,
@@ -869,7 +882,10 @@ What that means, and the tests that hold it (plan 8):
normalisation.
- For generated trees and rules, apply then undo restores every file, and
nothing is lost in between (property test, `KRINO_PROPERTY_RUNS`).
-- Every value of every enum the output, the log or planning depends on is
+- For generated trees and rules, apply then undo also restores every
+ directory, and the test fails if too few cases apply anything.
+- Every value of the enums planning, the log and the summary depend on
+ (`plan.Kind`, `config.ActionKind`, `config.Conflict`, `scan.Reason`) is
handled: tests read the constants from source.
- `make vulncheck` checks the standard library and dependencies against
the Go vulnerability database; `make race` runs the tests under the race
diff --git a/man/krino.1 b/man/krino.1
index fb6731f..246184b 100644
--- a/man/krino.1
+++ b/man/krino.1
@@ -73,7 +73,7 @@ prints the plan and asks how to proceed
.Fl y
applies the plan without asking;
.Fl n
-prints it and changes nothing.
+prints it and changes no file.
If standard input is not a terminal and neither is given,
.Nm
refuses rather than guess.
@@ -84,7 +84,8 @@ Apply the plan without asking.
Cannot be combined with
.Fl n .
.It Fl n
-Dry run: print the plan and exit without changing anything.
+Dry run: print the plan and exit without changing any file.
+It may refresh the keyword cache, which only makes the next run faster.
.It Fl v
Also list files that were skipped as unmatched, ignored or busy, and show
the full reason a test matched or not.
@@ -445,6 +446,20 @@ cheaper test in the same condition already decided the rule did not match.
is a full trace of every test, not a cost-faithful preview of what
.Fl n
would actually spend time on.
+.Pp
+Unicode format characters that are invisible but not controls (a zero-width
+space, U+FEFF, a soft hyphen) are printed as they are, so two names can look
+the same.
+Wrapping counts characters, not terminal columns: a name in a wide script
+(CJK) or with many combining marks can run past the terminal's width.
+.Pp
+A second interrupt exits at once: every step completed so far is logged
+and can be undone, but the step in flight when it arrived is not logged.
+.Pp
+Undo identifies a file by its directory and its path within it.
+A file one directory's rules move into another included directory, which
+sorts it again in the same run, is two files to undo; the first may then be
+refused as missing.
.Sh SEE ALSO
.Xr krino.conf 5
.Pp