From 7595a9d4d0d390b5bea8a841201c004edc931c54 Mon Sep 17 00:00:00 2001 From: Nils Lehnen <30603423+iderex@users.noreply.github.com> Date: Sun, 23 Aug 2026 04:24:55 +0200 Subject: [PATCH] The help output names the three documents an operator is owed An operator who runs the binary and never opens the repository had no route to the intended-use notice, the licence or the privacy document. The runner knew where all three were and said nothing about any of them, so for that reader the three files did not exist. The last paragraph of the usage text now names them. It names rather than restates, because a paraphrase printed by a binary somebody downloaded months ago is a copy of a document that has since moved on and the reader cannot tell which of the two they hold. The two limits this rests on are written at the declaration rather than only in the issue. A notice is not a control and printing it prevents nothing, so it should not be counted as a thing that stops misuse when somebody later asks what does. And a notice an operator has to run a verb to see is weaker than one sitting in the download beside the binary, which is why both routes are wanted rather than either alone. The guard is here because nothing else in the tree holds these strings. The paths leg of the invariants scan takes this repository's own documents as its subject, which is the files at the root and everything under docs/, and a path named inside the runner is outside that by the leg's own reckoning. The test asserts both halves of the walk an operator makes: that the text names the file, and that the file is in the tree to be found. Part of #36. It closes neither clause of that done-condition, which asks for the version output and the release archive as well, and both of those wait on something that does not exist yet. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com> --- cmd/lab/main.go | 29 +++++++++++++++++++++++++++++ cmd/lab/main_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/cmd/lab/main.go b/cmd/lab/main.go index 3b56333..b6d4936 100644 --- a/cmd/lab/main.go +++ b/cmd/lab/main.go @@ -50,6 +50,31 @@ const ( exitCannot = 2 ) +// The three documents the last paragraph of the usage text names. They are +// named rather than restated, because a paraphrase printed by a binary somebody +// downloaded months ago is a copy of a document that has since moved on, and the +// reader has no way to tell which of the two they are holding. +// +// TWO LIMITS THAT BELONG HERE RATHER THAN ONLY IN THE ISSUE. A notice is not a +// control. Printing it stops nothing, and it should not be counted as a thing +// that prevents misuse when somebody later asks what does. And a notice an +// operator has to run a verb to see is weaker than one sitting in the download +// beside the binary, because the operator who most needs it is the one who runs +// the thing without asking it for help first. Both routes exist for that +// reason rather than either alone; the download half is issue #36 and has no +// archive to be carried in yet. +// +// Nothing outside this package holds these strings to the tree. The paths leg +// of the invariants scan reads this repository's own documents, which is the +// files at the root and everything under docs/, and a path named inside the +// runner is outside that subject by the leg's own reckoning. So the guard is +// the test beside this declaration and there is no second one. +var documentsAnOperatorIsOwed = []string{ + "NOTICE.md", + "LICENSE", + "docs/privacy.md", +} + const usage = `lab reads this repository and reports what it examined. lab check [path] walk the tree at path, default ".", and report @@ -58,6 +83,10 @@ const usage = `lab reads this repository and reports what it examined. lab help print this text lab writes nothing to the tree it reads. + +NOTICE.md says what this program is for, LICENSE carries the terms it is under, +and docs/privacy.md says what stays on the host. Reading them is on you; this +text only says where they are. ` // THIS IS WHERE THE RUNNER READS THE TIME, and it is read once. Everything diff --git a/cmd/lab/main_test.go b/cmd/lab/main_test.go index 4c152f7..4e258f0 100644 --- a/cmd/lab/main_test.go +++ b/cmd/lab/main_test.go @@ -3,6 +3,8 @@ package main import ( "bytes" "errors" + "os" + "path/filepath" "strings" "testing" "time" @@ -215,3 +217,33 @@ func TestTheDocumentedCodesAreTheNumbersTheRecordFixes(t *testing.T) { } } } + +// TestHelpNamesTheDocumentsAnOperatorIsOwed holds the last paragraph of the +// usage text to the three files it points at. An operator who reads that +// paragraph and then looks for one of the files is the reader this asserts for, +// and both halves of the walk they make are here: that the text names the file, +// and that the file is in the tree to be found. +// +// The second half is the one that earns its place. A pointer in the runner is +// outside the subject of the invariants paths leg, which reads the files at the +// root and everything under docs/ and holds those to the paths they name. So a +// document deleted or moved under this paragraph reddens nothing anywhere else, +// and a binary would go on telling operators to read a file that is not there. +// +// What it does not judge is whether any of the three says what it should. That +// is a reading of prose and no test here makes it. +func TestHelpNamesTheDocumentsAnOperatorIsOwed(t *testing.T) { + var out, errOut bytes.Buffer + if got := run([]string{"help"}, &out, &errOut, ordinary(realWalk)); got != exitClean { + t.Fatalf("exit code %d, want %d", got, exitClean) + } + + for _, document := range documentsAnOperatorIsOwed { + if !strings.Contains(out.String(), document) { + t.Errorf("the help output does not name %s:\n%s", document, out.String()) + } + if _, err := os.Stat(filepath.Join("..", "..", filepath.FromSlash(document))); err != nil { + t.Errorf("the help output names %s and it is not in this tree: %v", document, err) + } + } +}