Synopsis:
This page shows the Rust executable run against this project's own tree, in the 11
invocations the page structure fixes.
-
Every block below is captured output. Nothing is paraphrased, reconstructed from the
specification, or edited after capture.
-
Seven invocations search
md, ixx, cpp, rs - the extension list is
cumulative, so this run covers the project's documents, the C++ sources, and its own.
Two divergences from the page structure closed in the document's favour rather than the
driver's, which is the reverse of this project's usual direction.
- The fixed set went from six invocations to eleven, and
[ext] became cumulative.
- Nothing in the driver changed when they closed - the document moved to where the code already was.
The cumulative list narrows what a reader may compare against the C++ capture, and
Section 4 says how far.
- This capture's path list contains the C++ one rather than diverging from it, but a Rust source interleaves with documents both runs report - so the lists agree as sets and not as sequences.
- One invocation of the seven escapes that limit: block 3.6 reads the project root where no implementation's sources sit, and every run reports the same eight paths in the same order.
- The capture is dated, and a page whose counts disagree with a fresh run is stale rather than wrong.
1. What the Demonstration Runs
tests/demonstration.rs runs the built executable against this project's own
tree, rooted at Spec_driven_TextFinder/, and shows the 11 invocations
Page_Structure.md §7.1 fixes. Seven of them supply
-P . -p "md, ixx, cpp, rs", so each search covers the project's documents
together with the C++ sources and this implementation's own - [ext] being
cumulative, per Page_Structure.md §0. The other four invocations are this
implementation's own, two rooted in its component directories, one in its structure
document, and one asking for help.
Every block below is captured output. Nothing on this page is paraphrased, reconstructed
from the specification, or edited after capture, per Page_Structure.md §7.3. The
capture is dated, and Section 5 says what that means for a reader comparing it against a
fresh run.
Each block takes the same six parts, per Page_Structure.md §7.2: the purpose in one or
two lines, the command line echoed as
$ <executable> <arguments>, at most 14 output lines followed by a
... N more line where N is the number withheld, the run summary of
Spec_TextFinder.md §3.6 when that excerpt withheld it, each stderr line prefixed
[stderr], and the total line count with the exit code. The count is of stdout
for the whole run rather than of the excerpt, so N plus 14 equals it whenever a
... N more line appears and no stderr line was shown.
The fifth part is the newest and exists because of where §3.6 puts its line. The
summary is the last thing a traversing run writes, so on every invocation emitting more than
14 lines the excerpt ends before reaching it, and a block that stopped there would withhold
the one line reporting what the run reached. It is shown after the ... N more
line and counted among the lines that line withholds.
This page carried two divergences from the mandate for several revisions, and both have
since been retired in the document's favour rather than the driver's.
- Eleven invocations, not six. §7.1 fixed a set of six while this
driver ran 11. The table now fixes eleven - seven that take
-P . -p "md, [ext]" and share their work, and four that are each
implementation's own.
- Four extensions, not one. §0 gave Rust the value
rs for [ext] while this driver searched
md, ixx, cpp, rs. [ext] is now cumulative - each language's
own extensions appended to those of the languages before it - so this driver's list is
what §0 fixes. What that costs is stated in Section 4 rather than left to a
reader to discover.
The blocks below are still what the executable actually produced, since §7.3 forbids
reconstructing a result from a document. Nothing in the driver changed when the two
divergences closed; the document moved to where the code already was, which is the reverse
of this project's usual direction and worth saying plainly.
Two rules govern which patterns a demonstration may use, and this set satisfies both. Every
pattern stays inside the portable subset of Spec_TextFinder.md §6.1, and none of the
patterns that match uses ., a character class, or a class escape, so none turns
on which engine an implementation names. And every pattern has something to find in every
implementation: too large is an announcement §3.4 fixes,
Spec_TextFinder.md names a document every implementation searches, and
Cargo names a file this one's structure document fixes. Invocation 8's
^pub is the exception that proves the rule: it is a Rust keyword, so that
invocation demonstrates Rust and is confined to two Rust files by its own
-P arguments rather than pretending to a wider search.
2. The Header
The driver writes four fields before the first block: the date of the capture, the path of
the executable, the search root, and the extension list. This is the whole of the header.
Rust_TextFinder demonstration
date: 2026-09-15
executable: c:/github/JimFawcett/NewSite/Code/Projects/Spec_driven_TextFinder/Rust_Spec_driven_TextFinder/target/debug/rust_textfinder.exe
root: c:/github/JimFawcett/NewSite/Code/Projects/Spec_driven_TextFinder
extensions: "md, ixx, cpp, rs"
The date comes from run_demo.bat through the
TEXTFINDER_DEMO_DATE environment variable, since the runner can ask the shell
for a local date and the driver cannot: the standard library supplies a
SystemTime and no calendar. The driver falls back to a civil date it computes
from the UTC clock, which reads as the previous day late in the evening, so a capture taken
any other way carries a date this one does not.
The executable path is what CARGO_BIN_EXE_rust_textfinder resolved to at
compile time, and the root is two directory levels above the package manifest. Both are
rendered with forward slashes on every platform, matching the rule §3.4 fixes for block
lines, which is a courtesy to a reader comparing headers rather than a requirement.
3. The Eleven Blocks
3.1 No Switch at All
The bare command line of Spec_TextFinder.md §3.1. It names no work, so
rust_textfinder lists the options a real invocation would start from and exits
0 without traversing. Look for the /v line: it reads false,
because this listing reports an option set in which /v was never set.
1. No switch at all. The command line names no work, so rust_textfinder lists the
options a real invocation would start from and exits 0 (Spec_TextFinder.md §3.1).
$ rust_textfinder
/P .
/p
/r .
/s true
/h true
/v false
/H false
/n false
/L false
9 line(s), exit 0
Nine lines, matching the nine §5.3 fixes, in §5 table order. The /p
line carries no value and no trailing space, which is the rule that lets this text serve as
a test fixture. These nine bytes for byte are what the C++ implementation prints, since
§5.3 fixes the form and neither implementation chooses it.
3.2 The Default Expression
The no-content case of §3.3: the default /r of . with neither
/n nor /L needs no file content, so every selected file of
non-zero size is reported by its path line alone and none of them is opened.
2. Default expression. The default /r of . with no /n or /L needs no file content,
so each selected file is reported by its path line alone (Spec_TextFinder.md §3.3).
$ rust_textfinder -P . -p "md, ixx, cpp, rs"
Constitution.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Fix_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline_TestDriver.cpp
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline_UnitTest.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Fix_Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav_TestDriver.cpp
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav_UnitTest.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Prompts_Fix_Spec_Cpp_TextFinder_Output.md
... 57 more
accessed 70 files, 36 directories
71 line(s), exit 0
71 lines for 70 selected files, the extra one being the run summary. What the count alone
does not show is that this invocation performed no reads: the size test ran on filesystem
metadata and nothing else, which is why this is among the fastest of the 11 and why a binary
file among these paths would be reported rather than skipped. The 14 shown are the first 14
in filesystem order, which is the order §3.2 leaves to the platform.
accessed 70 files, 36 directories is the arithmetic of the no-content case laid
bare: every selected file matched, so the file count equals the number of path lines above
it. A zero-length file would break that equality - counted but blockless - and this tree
holds none among these extensions.
The target/ directory holding the build this run used contributes nothing to
that count, since target is one of the 11 default skip-list names. So is
build, which prunes the C++ build tree the same way.
3.3 The Two-Level Block
The block form of §3.4 with both optional fields on: a path written once, then an
indented detail line per match carrying the line number and the line's text. The pattern
too large is one of the four announcements the specification fixes, so it
appears as a literal in the documents and in the sources of every implementation.
3. The two-level block of §3.4: a path written once, then an indented detail line
per match carrying the line number and the line's text.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r "too large" -n true -L true
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Fix_Spec_Cpp_TextFinder_Dirnav.md
73 - [Spec_TextFinder.md](../../Spec_TextFinder.md) gained: symlink and non-file/non-directory root handling in §3.2; the three admission tests in §3.3 — 10 MB, no NUL byte, valid UTF-8 — with whole-file reading justified by "skipped entirely rather than searched in part"; path rendering and a four-row announcement table in §3.4 (`searched`, `skipped`, `too large`, `cannot open`) with the `/h` gating; the dotfile rule, extension case-sensitivity, the non-empty-regex requirement, and the new `/h` meaning across the `/p`, `/r`, and `/h` rows of §5; an empty-`/r` row in §5.2; and the NUL test named in §7.
80 - - **`too large` and `cannot open` ignore `/h`; `searched` and `skipped` obey it.** Item 6 suppressed announcements but item 3 wanted failures visible; silencing a 10 MB skip under the default would hide requested work that did not happen.
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Spec_Cpp_TextFinder_Dirnav.md
84 - No file announcement accompanies that block. `searched` reports a file that was read and matched nothing and `skipped` reports one a content test rejected, and in this case neither happened — every selected file matches — so the library emits neither, whatever `/h` says. A selected file of zero size produces no block and draws no announcement either. The size test still runs on filesystem metadata, so a file above the limit still draws the error announcement `too large`, and one whose metadata cannot be read still draws `cannot open`.
117 - The file count is incremented in the one place a file is examined: immediately after the `/p` test of §6 admits it and before its size is taken. Everything §3.6 excludes is therefore excluded by construction rather than by a second test — an entry refused by `/p`, a symbolic link, an entry beneath a pruned directory, an entry whose name will not render, and an entry that is neither a regular file nor a directory all fail or bypass that test and never reach the increment, though the last two draw `cannot open` on the way past. A file admitted and then announced `too large` or `cannot open` is counted, the increment standing ahead of both.
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav.ixx
264 - if (size > sizeLimit) { emit("too large " + displayPath(file)); return; }
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav_UnitTest.ixx
315 - check.equal(relative(search(root / "big.txt", commands), root), "too large big.txt",
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Prompts_Fix_Spec_Cpp_TextFinder_Output.md
33 - 1. Parent §3.4 contradicted itself in consecutive sentences — "under the default /h `true` no file announcement appears and the output holds match records alone," then "`too large` and `cannot open` are emitted whatever /h says." Both are file announcements.
58 - > 1. treat "too large" and "cannot open" as error announcements not file announcements. Make any name changes appropriate to this new definition
78 - Parent §3.4 now distinguishes **file announcements** — `searched` and `skipped`, gated on `/h` — from **error announcements** — `too large` and `cannot open`, emitted whatever `/h` says — in separate tables, which retires the self-contradiction: under the default the output holds match records and error announcements. The new names propagated to the `/h` row, the §5.1 help text, Dirnav §5 rule 6 and §8, and Entry's diagnostics section. Parent §3.4 also gained the terminator rule moved out of the Output spec: a single LF for every record and announcement on every platform, with implementations obliged to stop their runtime translating it.
... 67 more
accessed 70 files, 36 directories
81 line(s), exit 0
81 lines over 19 files: 19 path lines, 61 detail lines, and the run summary. The
two-space indent, the
- between the number and the text, and the path appearing once
above its details are all §3.4 rather than choices this implementation made. Line 255
of Cpp_TextFinder_Dirnav.ixx is the C++ implementation emitting the
announcement this search is looking for, which is the demonstration reading the other
implementation's source.
The detail lines are long because the matched lines are long. §3.4 fixes the line's
text verbatim with no truncation, so a match inside a 600-character Markdown paragraph
produces a 600-character detail line. The arrows at the top right of the block reach the
right-hand end without scrolling to the bottom.
3.4 The Same Search with /L false
Dropping /L leaves the line number alone on each detail line. The same 19
files, the same 61 matches, the same 81 lines, and the same two counts in the summary:
/n and /L select what a detail line carries and neither decides
whether there is one, nor what the run reached.
4. The same search with /L false, leaving the line number alone on each detail line.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r "too large" -n true
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Fix_Spec_Cpp_TextFinder_Dirnav.md
73
80
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Spec_Cpp_TextFinder_Dirnav.md
84
117
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav.ixx
264
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav_UnitTest.ixx
315
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Prompts_Fix_Spec_Cpp_TextFinder_Output.md
33
58
78
... 67 more
accessed 70 files, 36 directories
81 line(s), exit 0
The 14 shown here reach one file further than 3.3's did, which is the only visible effect of
the shorter lines. Demo_9-14-2026.md appears as a path line with no detail line
beneath it in the excerpt, because its first match falls past the 14-line cut rather than
because it has none.
3.5 Path-Only Blocks
Which documents cite the parent specification. With neither /n nor
/L, every block is its path line, and rule 2 of the emission rules applies: the
loop over a file's lines returns at the first match, so a file matching 40 times costs the
same as one matching once.
5. Which documents cite the parent specification. Neither /n nor /L, so every block
is its path line and no path is written twice.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r Spec_TextFinder\.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Fix_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline_UnitTest.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Fix_Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Prompts_Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/Spec_Cpp_TextFinder_Dirnav.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Dirnav/src/Cpp_TextFinder_Dirnav_UnitTest.ixx
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Prompts_Fix_Spec_Cpp_TextFinder_Output.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Prompts_Spec_Cpp_TextFinder_Output.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/Spec_Cpp_TextFinder_Output.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Output/src/Cpp_TextFinder_Output.ixx
... 39 more
accessed 70 files, 36 directories
53 line(s), exit 0
53 lines for 52 files against 3.2's 71, so 18 of the selected files cite the parent
specification nowhere - while the summary reports the same
accessed 70 files, 36 directories as 3.2, since the same files were examined
whatever the expression found in them. The escaped dot in Spec_TextFinder\.md is the only
metacharacter in the whole pattern set outside invocations 8 and 10, and §6.1 puts a
class escape inside the subset all four engines accept alike.
3.6 One Level Deep
The same search with -s false, which enters no subdirectory. What remains is
the project root's own documents.
6. The same search one level deep, /s false entering no subdirectory.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r Spec_TextFinder\.md -s false
Page_Structure.md
Project_Tree.md
Prompts_Fix_Constitution.md
Prompts_Fix_Spec_TextFinder.md
Prompts_Page_Structure.md
Prompts_Spec_TextFinder.md
README.md
Text_Tone.md
accessed 13 files, 1 directories
9 line(s), exit 0
Nine lines and no ... N more, since a run emitting 14 lines or fewer shows all
of them - eight paths and the summary, whose accessed 13 files, 1 directories
is the clearest statement on this page of what -s false does: one directory
read, no descent. Spec_TextFinder.md itself is absent from this list, and its absence is
the rule rather than an omission: the pattern matches the file's own name in a citation, and
the file cites its own name nowhere.
These eight are the project-level documents, so this is the one invocation whose output does
not move when either implementation's sources change. It is also the one that moves when a
document is added at the project root, which Section 5 covers.
3.7 Announcements and the Resolved Option Set
-h false adds a line for each file that matched nothing - the files 3.5 left
silent - alongside the listing -v true writes ahead of the search.
7. /h false adds a line for each file that matched nothing - the files case 5 left
silent - alongside the resolved option set from /v true.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r Spec_TextFinder\.md -h false -v true
/P .
/p md, ixx, cpp, rs
/r Spec_TextFinder\.md
/s true
/h false
/v true
/H false
/n false
/L false
searched Constitution.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Fix_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Prompts_Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/Spec_Cpp_TextFinder_Cmdline.md
Cpp_Spec_driven_TextFinder/Cpp_Spec_driven_Cmdline/src/Cpp_TextFinder_Cmdline.ixx
... 66 more
accessed 70 files, 36 directories
80 line(s), exit 0
80 lines against 3.5's 53: nine listing lines plus 18 searched announcements
added to the same 52 paths and the same summary. The listing's /r line carries the expression
verbatim as typed, backslash included, and its /h and /v lines
report the values that produced this output.
searched Constitution.md is the first line after the listing, which shows
announcements interleaved with blocks in traversal order rather than collected at the end.
The 18 announcements are exactly 3.2's 70 files less 3.5's 52, which is the arithmetic
§3.4 promises: a file announcement reports only a file that produced no block, so the
two sets partition the selected files and neither path appears twice.
This is the one block where that arithmetic can be checked against the program's own count
rather than against another block. Under -h false every examined file appears
exactly once, so the 52 paths plus the 18 announcements must equal the 70 the summary
reports - and they do. The summary is not an announcement and takes no part in that
partition; it reports the total the two sides add up to.
3.8 Two Roots
Two -P arguments, traversed in the order given. Each path begins with the root
whose subtree holds it, per §3.4, and the skip list prunes target/ beneath
both. This is the one invocation whose pattern names a construct of one language, so it is
held to two Rust directories rather than let loose on the tree.
8. Two roots, traversed in the order /P gave them. Each path begins with the root
whose subtree holds it, and the skip list prunes target/ beneath both.
$ rust_textfinder -P Rust_Spec_driven_TextFinder/Rust_Spec_driven_Cmdline -P Rust_Spec_driven_TextFinder/Rust_Spec_driven_Output -p rs -r "^pub " -n true -L true
Rust_Spec_driven_TextFinder/Rust_Spec_driven_Cmdline/src/lib.rs
8 - pub struct ProgramCommands {
63 - pub fn usage_line() -> String {
69 - pub fn help_text() -> String {
73 - pub fn options_text(commands: &ProgramCommands) -> String {
106 - pub fn parse(args: &[String]) -> Result<ProgramCommands, String> {
Rust_Spec_driven_TextFinder/Rust_Spec_driven_Output/src/lib.rs
15 - pub struct StdoutSink {
accessed 4 files, 4 directories
9 line(s), exit 0
The five detail lines under Rust_Spec_driven_Cmdline/src/lib.rs are that
library's whole public interface, which the
Cmdline page lists in its
Section 1. The anchor ^ is what keeps the count to five: pub
appears 12 more times in that file as a field, each indented and so not at a line's start.
unit_tests.rs sits beside both files and neither run reports it, since
#[cfg(test)] keeps its items out of the public interface and its own items are
not pub. That is the filter being a regular expression and nothing cleverer:
the file was searched and matched nothing.
accessed 4 files, 4 directories is the block that shows why the count is not a
count of what was reported. Two files produced blocks and two did not - the two
unit_tests.rs files, hidden by the default /h true - so the file
count exceeds the paths above it by exactly the files /h hid. The four
directories are the two roots and the src/ beneath each.
3.9 An Unopenable Root
A root path that does not exist is announced and the run still exits 0. The second root is a
regular file, which §3.2 has searched as a single file rather than traversed.
9. A root path that cannot be opened is announced and the run still exits 0, while
an error announcement ignores /h true.
$ rust_textfinder -P no_such_directory -P Rust_Spec_driven_TextFinder/Rust_TextFinder_Structure.md -r Cargo
cannot open no_such_directory
Rust_Spec_driven_TextFinder/Rust_TextFinder_Structure.md
accessed 1 files, 0 directories
3 line(s), exit 0
Three lines showing four rules. The announcement appears under the default /h true,
because §3.4 gates file announcements on /h and error announcements on
nothing. It reaches stdout rather than stderr, since
rust_textfinder_dirnav emits it through the same Output as every
block line, and the binary neither formats nor inspects it. And the exit code is 0: a root
that could not be searched is a result, not a usage failure, so
rust_textfinder_entry has nothing to report.
The second root carries no -p filter, so the extension filter admits it. A
-p rs on the same command line would have dropped it silently, since a root
path escapes the skip list and does not escape the extension filter.
The fourth rule is the summary's, and this is the smallest block that states it:
accessed 1 files, 0 directories. The unopenable root counts as neither, no
attempt on it having reached the tests the count is about, while the root that resolved to a
regular file counts as one file and adds no directory. The uninflected 1 files
is §3.6's accepted cost, visible here rather than argued for.
3.10 A Malformed Expression
-r "pub fn (" is an unbalanced group that the regex crate will not
compile. §5.2 puts the option listing on stdout first, so the /r line
shows what failed, then the diagnostic on stderr.
10. A malformed expression. §5.2 puts the option listing on stdout first, so the /r
line shows what failed, then the diagnostic on stderr, and the exit code is 1.
$ rust_textfinder -P . -p "md, ixx, cpp, rs" -r "pub fn ("
/P .
/p md, ixx, cpp, rs
/r pub fn (
/s true
/h true
/v false
/H false
/n false
/L false
[stderr] invalid regex for switch: /r
[stderr] usage: rust_textfinder [/P path] [/p "ext, ext"] [/r regex] [/s bool] [/h bool] [/v bool] [/H bool] [/n bool] [/L bool]
9 line(s), exit 1
Nine lines on stdout and exit 1. Three rules are visible at once: the listing appears
whatever /v says, since the /v line itself reads
false; the diagnostic is a reason line followed by the §5.1 usage line,
with <executable> resolved to rust_textfinder; and the
listing reaches the stream ahead of the diagnostic, which the binary buys with an explicit
flush because the sink defers its writes.
The regex crate's own error text appears nowhere, and the specification gives
the reason: that text belongs to the crate, so a crate upgrade would alter this program's
output with no document in this tree recording the change. What the user gets instead is the
/r line above, carrying the expression as typed.
The run traversed nothing. Compilation happens at Dirnav::new, before the first
root path is visited, so a bad pattern costs no file reads.
3.11 The Help Text
/H true writes the text §5.1 fixes to stdout and exits 0, traversing
nothing. This block uses the / introducer where the others use
-, which §4 makes equivalent.
11. The help text of §5.1, written to stdout under /H, traversing nothing.
$ rust_textfinder /H true
usage: rust_textfinder [/P path] [/p "ext, ext"] [/r regex] [/s bool] [/h bool] [/v bool] [/H bool] [/n bool] [/L bool]
/P path (.) root path for traversal; repeat to add more root paths
/p "ext, ext" () comma-separated bare extensions to search; empty searches every file
/r regex (.) regular expression evaluated against each line
/s true|false (true) recurse into subdirectories
/h true|false (true) hide files that matched nothing; errors always appear
/v true|false (false) list the resolved option set before traversal
/H true|false (false) print this help and exit
/n true|false (false) add a detail line per match, carrying the line number
/L true|false (false) add a detail line per match, carrying the line text
A matching file prints its path on one line; /n and /L add indented detail
lines beneath it. A path is never printed twice. A search ends with a line
... 8 more
22 line(s), exit 0
22 lines, of which the first is the usage line invocation 10 sent to stderr.
help_text() is built from usage_line(), so the two agree by
construction rather than by proofreading, and the integration suite asserts that this
capture equals what the library returns.
It was 21 until §3.6 arrived. The block-form paragraph gained a sentence -
"A search ends with a line counting the files and directories it reached." - because a help
text that described the block form without naming that line would leave one line of every
run unexplained. The excerpt above cuts off mid-sentence at line 14, which is the 14-line
rule doing its job rather than a truncated capture.
The eight withheld lines are the rest of that sentence and the three paragraphs on
introducers, quoting, and the bare command line. §5.1 fixes every one of these 22 lines
byte for byte with <executable> as the only substitution, so a C++ capture
of this block differs from this one in exactly two words. This invocation traverses nothing,
so it writes no run summary of its own.
demonstration complete
4. Comparability
Page_Structure.md §7.4 states comparability against the part two runs share, and the
cumulative extension list of §0 is what narrows that part. This run searches
md, ixx, cpp, rs and the C++ run searches md, ixx, cpp, so this
capture's path list contains that one's rather than diverging from it: every file the C++
run selects, this run selects too, plus the Rust sources.
The paths-agree claim holds only where two runs select the same files.
§7.4 now says so outright, and the consequence is sharper than it sounds. It does not
merely exempt the extra Rust paths; it exempts the order of the shared documents too,
because a path list is one depth-first walk and a Rust source selected here interleaves with
documents both runs report. Rust_Spec_driven_Cmdline/src/lib.rs falls between
two documents the C++ capture reports consecutively, so the two lists agree as sets over the
documents and not as sequences. What follows is what survives that.
| Comparable across implementations |
Is this run's own |
| Block 3.6's eight paths, in the same order, that invocation being the only one of the seven where two runs select the same files |
Every other invocation's path order, per §7.4 |
| Block 3.1's nine listing lines and block 3.11's 22 help lines, but for the executable name §5.1 parameterizes |
Every file count but block 3.6's, since the file sets differ |
| Block 3.10's two stderr lines in full, but for that same name |
The header's executable and search root |
| The exit code of every invocation, §3.4 fixing all three values |
The echoed command line's executable and extension list |
| The block form itself: the indent, the field separator, and the path written once |
The two counts in every run summary, which include this implementation's own sources |
| The form of the run summary: one line, two decimal counts, neither noun inflected |
Blocks 3.8 and 3.9, rooted in this implementation's own directories |
One invocation of the seven escapes that limit, and §7.4 now names it as the only one.
Block 3.6 reads the project root with -s false, where no implementation's
sources sit, so every run selects the same 13 documents and reports the same eight paths in
the same order - and every run's summary reports
accessed 13 files, 1 directories. A reader wanting to check Spec_TextFinder.md
§6's consistency guarantee against captured output has that block and no other.
It would be easy to blame the cumulative list for that, and §7.4 separates the two
things. Leaving 3.6 the only block whose whole path list agrees is not the cumulative list's
doing: a per-language list would leave it the only one too, since two runs would still select
different sources and still interleave them among the documents. What the cumulative list
costs is narrower - it makes each run's file set a superset of the ones before it, so no two
of the four pages report the same number of files.
What it buys is the record. A per-language list would make this page a search of Rust files
alone - a fraction of the tree it is rooted at, and a shrinking fraction as languages are
added. §0 takes the wider record and §7.4 states the narrower claim that record
supports, rather than keeping a wider claim by narrowing what this page searches.
A mismatch in the comparable part is a defect in one of the two implementations or an
ambiguity in Spec_TextFinder.md, and the page reporting it says which. A
disagreement in the exit code in particular is a defect rather than a difference of
convention, since the three values are fixed rather than left to the implementation.
Comparing two implementations means comparing captures taken over the same tree state on the
same platform. Spec_TextFinder.md §6 permits a line-for-line comparison only under that
condition, because §3.2 leaves a directory's entries in filesystem order, and §6
now scopes the guarantee to stdout, so stderr wording is each implementation's own.
5. The Capture Is Dated
Seven of the 11 invocations count files in this project, so their output moves when a
document is added to the tree or removed from it, and the run summary of §3.6 moves
with it in each of the eight that traverse. This page therefore states the date of its
capture, 2026-09-16, and a page whose counts disagree with a fresh run is stale rather than
wrong.
The summary sharpens that. Before §3.6, a reader comparing this page against a fresh
run had to count the block's lines to notice the tree had moved; now every traversing block
ends in two numbers that say so outright, and the line count beneath it is one larger than
it was in the capture this page replaced.
The tree moves for reasons the program has nothing to do with, and this page carries the
clearest case of it. The capture it replaced, Demo_9-15-2026.md, reported 60
lines for invocation 2 where this one reports 71, and 47 lines for invocations 3 and 4 where
this one reports 81. One line of each increase is the run summary; the rest is the tree.
§7.5 has the replaced capture deleted rather than kept beside this one, since two dated
captures of one implementation invite a reader to compare counts that were never taken over
the same tree.
The earlier capture was itself one of the files invocation 3 reported. A demonstration of a
search for too large, written into the tree it searches, becomes a file that
matches too large - which is worth more than the arithmetic, because it is the
plainest statement of why §7.5 dates a capture rather than treating one as a fixed
expected result.
Invocation 6 is the one that did not move. It searches the project root alone with
-s false, and no file was added there between the two captures.
A page rebuilt after the tree changes is recaptured in full rather than edited in place,
since the counts appear both in the blocks and in the prose around them. Recapturing is one
command:
run_demo.bat
6. Source
demonstration.rs in full, then its runner. The 11 case calls at the
bottom are the 11 blocks above, and their note strings are the prose each capture carries.
The driver is a #[test] that asserts nothing and fails nothing, which is a
deliberate reading of §6.2: a demonstration shows what the program produces, and a
demonstration that asserted its own output would be a third test suite whose fixtures go
stale every time a document joins the tree. run_demo.bat passes
--nocapture so the harness lets the output through.
Rust_Spec_driven_TextFinder_Entry/tests/demonstration.rs
//! Demonstration, per Spec_TextFinder.md section 6.2. It runs the built executable against
//! this project's own tree and shows what it produces. It asserts nothing and fails nothing;
//! its output moves as the tree changes, so a capture states the date it was taken.
//!
//! Run through run_demo.bat.
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
const EXECUTABLE: &str = env!("CARGO_BIN_EXE_rust_textfinder");
const EXTENSIONS: &str = "md, ixx, cpp, rs";
const SHOWN: usize = 14;
fn project_root() -> PathBuf {
// <project>/Rust_Spec_driven_TextFinder/Rust_Spec_driven_TextFinder_Entry
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
match manifest.parent().and_then(Path::parent) {
Some(root) => root.to_path_buf(),
None => manifest.to_path_buf(),
}
}
fn forward(path: &Path) -> String {
path.to_string_lossy().replace('\\', "/")
}
/// The local date when run_demo.bat supplies it, the UTC civil date otherwise,
/// so that a capture dates itself however the demonstration was started.
fn today() -> String {
if let Ok(supplied) = std::env::var("TEXTFINDER_DEMO_DATE") {
if !supplied.is_empty() {
return supplied;
}
}
let seconds = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(since) => since.as_secs() as i64,
Err(_) => 0,
};
let days = seconds.div_euclid(86_400) + 719_468;
let era = days.div_euclid(146_097);
let day_of_era = days.rem_euclid(146_097);
let year_of_era =
(day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
let shifted = (5 * day_of_year + 2) / 153;
let day = day_of_year - (153 * shifted + 2) / 5 + 1;
let month = if shifted < 10 { shifted + 3 } else { shifted - 9 };
let year = year_of_era + era * 400 + if month <= 2 { 1 } else { 0 };
format!("{year:04}-{month:02}-{day:02}")
}
/// Restores the quotes the shell removed, so the echoed command line can be retyped.
fn quoted(argument: &str) -> String {
if argument.contains(' ') || argument.contains(',') {
format!("\"{argument}\"")
} else {
String::from(argument)
}
}
fn case(number: usize, note: &[&str], args: &[&str]) {
let indent = " ".repeat(number.to_string().len() + 2);
println!("{number}. {}", note.join(&format!("\n{indent}")));
let command: Vec<String> = args.iter().map(|argument| quoted(argument)).collect();
println!("{}", format!(" $ rust_textfinder {}", command.join(" ")).trim_end());
println!();
let outcome = Command::new(EXECUTABLE).args(args).current_dir(project_root()).output();
let outcome = match outcome {
Ok(outcome) => outcome,
Err(error) => {
println!(" [the executable could not be run: {error}]\n");
return;
}
};
let stdout = String::from_utf8_lossy(&outcome.stdout).into_owned();
let mut lines: Vec<String> = stdout.lines().map(String::from).collect();
let emitted = lines.len();
for line in String::from_utf8_lossy(&outcome.stderr).lines() {
lines.push(format!("[stderr] {line}"));
}
for line in lines.iter().take(SHOWN) {
if line.is_empty() {
println!();
} else {
println!(" {line}");
}
}
if lines.len() > SHOWN {
println!(" ... {} more", lines.len() - SHOWN);
// Page_Structure.md section 7.2 part 4: the run summary is the last line a traversing
// run writes, so the excerpt above never reaches it. Show it rather than withhold it.
if emitted > SHOWN && lines[emitted - 1].starts_with("accessed ") {
println!(" {}", lines[emitted - 1]);
}
}
println!();
println!(" {emitted} line(s), exit {}", outcome.status.code().unwrap_or(-1));
println!();
}
#[test]
fn demonstration() {
println!();
println!("Rust_TextFinder demonstration");
println!(" date: {}", today());
println!(" executable: {}", forward(Path::new(EXECUTABLE)));
println!(" root: {}", forward(&project_root()));
println!(" extensions: \"{EXTENSIONS}\"");
println!();
case(
1,
&[
"No switch at all. The command line names no work, so rust_textfinder lists the",
"options a real invocation would start from and exits 0 (Spec_TextFinder.md §3.1).",
],
&[],
);
case(
2,
&[
"Default expression. The default /r of . with no /n or /L needs no file content,",
"so each selected file is reported by its path line alone (Spec_TextFinder.md §3.3).",
],
&["-P", ".", "-p", EXTENSIONS],
);
case(
3,
&[
"The two-level block of §3.4: a path written once, then an indented detail line",
"per match carrying the line number and the line's text.",
],
&["-P", ".", "-p", EXTENSIONS, "-r", "too large", "-n", "true", "-L", "true"],
);
case(
4,
&["The same search with /L false, leaving the line number alone on each detail line."],
&["-P", ".", "-p", EXTENSIONS, "-r", "too large", "-n", "true"],
);
case(
5,
&[
"Which documents cite the parent specification. Neither /n nor /L, so every block",
"is its path line and no path is written twice.",
],
&["-P", ".", "-p", EXTENSIONS, "-r", "Spec_TextFinder\\.md"],
);
case(
6,
&["The same search one level deep, /s false entering no subdirectory."],
&["-P", ".", "-p", EXTENSIONS, "-r", "Spec_TextFinder\\.md", "-s", "false"],
);
case(
7,
&[
"/h false adds a line for each file that matched nothing - the files case 5 left",
"silent - alongside the resolved option set from /v true.",
],
&["-P", ".", "-p", EXTENSIONS, "-r", "Spec_TextFinder\\.md", "-h", "false", "-v", "true"],
);
case(
8,
&[
"Two roots, traversed in the order /P gave them. Each path begins with the root",
"whose subtree holds it, and the skip list prunes target/ beneath both.",
],
&[
"-P",
"Rust_Spec_driven_TextFinder/Rust_Spec_driven_Cmdline",
"-P",
"Rust_Spec_driven_TextFinder/Rust_Spec_driven_Output",
"-p",
"rs",
"-r",
"^pub ",
"-n",
"true",
"-L",
"true",
],
);
case(
9,
&[
"A root path that cannot be opened is announced and the run still exits 0, while",
"an error announcement ignores /h true.",
],
&["-P", "no_such_directory", "-P", "Rust_Spec_driven_TextFinder/Rust_TextFinder_Structure.md", "-r", "Cargo"],
);
case(
10,
&[
"A malformed expression. §5.2 puts the option listing on stdout first, so the /r",
"line shows what failed, then the diagnostic on stderr, and the exit code is 1.",
],
&["-P", ".", "-p", EXTENSIONS, "-r", "pub fn ("],
);
case(
11,
&["The help text of §5.1, written to stdout under /H, traversing nothing."],
&["/H", "true"],
);
println!("demonstration complete");
}
run_demo.bat
@echo off
rem run_demo.bat - runs the rust_textfinder demonstration and reports its status
rem
rem Spec_TextFinder.md §6.2: the runner builds what it is about to run, supplies the
rem capture date, announces the status the demonstration returned, and holds the console
rem after its summary.
setlocal enabledelayedexpansion
cd /d "%~dp0"
set FAILURES=0
echo building the workspace
cargo build --workspace --quiet
if errorlevel 1 (
echo build failed; the demonstration counts as failed
set FAILURES=1
goto :summary
)
for /f %%d in ('powershell -NoProfile -Command "Get-Date -Format yyyy-MM-dd"') do set TEXTFINDER_DEMO_DATE=%%d
echo.
echo === starting demonstration: demonstration
echo === captured %TEXTFINDER_DEMO_DATE%
cargo test -p rust_textfinder_entry --test demonstration -- --nocapture
set STATUS=!errorlevel!
echo === demonstration returned !STATUS!
if not "!STATUS!"=="0" set /a FAILURES+=1
:summary
echo.
echo !FAILURES! demonstration^(s^) failed
call :hold
exit /b !FAILURES!
rem --- §6.2: hold the console unless a capture suppressed it. ---
:hold
if not "%TEXTFINDER_NO_PAUSE%"=="" exit /b 0
echo.
pause
exit /b 0
quoted restores the quotes the shell removed, so every echoed command line can
be retyped as it appears. Without it block 3.2's command would read
-p md, ixx, cpp, rs, which a shell would hand the program as four arguments and
which would draw not a switch: ixx,.
case counts stdout lines before appending the stderr lines, which is why block
3.10 reports 9 while showing 11. The count §7.2 asks for is of the run's stdout, and the
[stderr] prefix keeps a diagnostic distinguishable from a record without
reordering either.
7. Prompt Records
This page carries none. Page_Structure.md §8 gives its last row to any record of
demonstration changes made at the language level, stated by role rather than by name, and
the turn that wrote this driver produced none. The
Process page says what
follows from that for the thread as a whole.