Spec-Driven

Spec-Driven Rust: Demonstration

the built executable run against this project's own tree, 2026-09-16

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.