Commit Graph

106 Commits

Author SHA1 Message Date
Nicolas Le Cam d554bef221 test(grep): cover the numeric pattern that -l silently swallowed
A non-numeric pattern stops at clap's parse error and falls back to raw
grep, so rtk and grep agree byte-for-byte whether or not `-l` is bound to
--max-len: the three existing cases still pass with the bug reintroduced.
Only a numeric pattern reaches the wrong answer -- rtk printed nothing and
exited 1 where grep listed the file.

Drop the clap-level test and the comment sentence with it. Develop's
test_grep_parse_files_with_matches_l already fails under the same mutation,
and -L never had a short binding on --max-len, so src/main.rs is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 01:53:30 +02:00
breisnerlopez 74ec07017f fix(grep): put --max-len before positionals in bench; reuse assert_eq_grep
Address review:
- benchmark.sh: with trailing_var_arg on extra_args, --max-len after the
  positionals was swallowed and forwarded raw to grep (unknown option ->
  bench FAIL). Move it before -rn/pattern so rtk parses it; verified 320KB
  -> 14.8KB (~95%) and that --max-len 40 vs 200 changes output size.
- grep_faithful_format_test: the inline cmp closure duplicated the existing
  assert_eq_grep helper (rtk_grep + grep_plain, same stdout/exit asserts);
  drop it in.
2026-09-08 15:30:49 +02:00
breisnerlopez 5681008ca9 fix(grep): free -l for GNU --files-with-matches instead of --max-len
`-l` was bound to RTK's `--max-len`, colliding with GNU grep where `-l`
means `--files-with-matches`. Running `grep -l <pattern>` made clap read
the pattern as a usize and error (`invalid value '<pattern>' for
'--max-len'`), dropping the command to a raw-grep fallback at 0% savings.

Drop the short binding. `-l`/`-L` now flow to `extra_args`, where the
existing `has_format_flag` already routes them to the grep passthrough
(GNU semantics, minimal output). `--max-len` keeps its long form and its
default of 80; nothing internal used the short.

- src/main.rs: remove `short = 'l'` from `Grep::max_len` + clap-parse
  regression test.
- tests/grep_faithful_format_test.rs: end-to-end test asserting
  `rtk grep -l/-L` is byte-identical to `grep -l/-L`, covering `-l`
  leading and trailing.
- scripts/benchmark.sh: the max-len bench used the old `-l 40` alias ->
  `--max-len 40`.
- docs/usage/FEATURES.md: drop the `-l` short from the grep options table.
2026-09-08 15:30:49 +02:00
Nicolas Le Cam f7495466ed Merge pull request #2515 from allanbatista/codex/fix-native-test-expression
[codex] fix native test expression passthrough
2026-09-08 03:37:13 +02:00
Nicolas Le Cam 80e45683be test(test): cover Commands::Test routing end to end
The unit tests exercise `is_native_test_expression` directly, so inverting or
dropping the branch that calls it left the suite green while `rtk test -d dir`
went back to `sh: 0: Illegal option -d`.

Run the binary instead: native expressions carry the system `test` exit code,
`!` is neither answered backwards nor allowed to swallow a command, argument
boundaries survive, and a command still reaches the test runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 03:23:44 +02:00
Nicolas Le Cam db98a064b1 Merge upstream/develop into up/diff-correctness
#3788 rewrote the piped-stream half of diff_cmd.rs (condense_stdin,
condense_unified_diff_strict) while this branch rewrote the file-versus-file
half (Hunk, compute_diff, the renderers). The two conflicts are positional:
both sides add code at the same offset. Resolved as a union -- develop's
imports plus this branch's, and both blocks kept whole.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 02:21:03 +02:00
Nicolas Le Cam ab0cf40112 Merge pull request #3788 from kylehgc/claude/upstream-issue-comments-4pky96
fix(diff): region parser for condense_unified_diff — budget-owned hunks, raw fallback
2026-09-08 01:22:52 +02:00
TheGlitching ebf4cbf539 fix(sqlfluff): always filter, and share one decision between both entry points
sqlfluff exits 1 whenever it finds violations, which is the normal case this
filter exists for. Two changes in the last push read that exit code as failure
and so disabled the filter exactly when it had something to compress:

- `sqlfluff_cmd` used `early_exit_on_failure()`, which dumps the injected JSON
  raw. On a 51-file corpus that is 9.8 KB where plain `sqlfluff lint` is 1.1 KB.
- `lint_cmd` substituted `result.stderr`, which is always empty: sqlfluff writes
  even fatal errors to stdout (verified against 2.3.5 and 4.3.0 - `Error:
  Unknown dialect 'NOPE'` on stdout, stderr empty, exit 2). Every violation was
  discarded in favour of a 25-byte `SQLFluff: failed (exit 1)`.

Both are one bug reached two ways, because both entry points restated the same
routing and format-flag logic and were then edited differently. Extract it into
`sqlfluff_cmd::plan`, which states once how rtk invokes sqlfluff and how it
reads the result back; `lint_cmd` now calls it instead of keeping a copy. The
exit code only disambiguates output the filter could not parse, so a fatal error
reaches the user verbatim while violations are still summarized.

Also in the report:

- Rank the `Violations:` section worst-file-first like every section above it.
  Iterating sqlfluff's emission order and taking the first 50 dropped exactly
  the file the summary ranked first.
- Build only the lines that get printed, instead of every line to keep fifty.
- Omit positions sqlfluff did not report rather than fabricating `:0:0`.
- Keep two path segments when no dbt root matches, so `migrations/orders.sql`
  and `reports/orders.sql` stay distinct in the section meant to be opened.
- Treat a missing `fixes` key as unknown rather than zero. sqlfluff 2.x omits it
  entirely, which silently suppressed the `sqlfluff fix` hint.
- Rename the savings test to the bound it actually asserts.

Tests cover both regressions against real captured output: 4.3.0 for the current
field names and fix data, 2.3.5 for the legacy names and the absent `fixes` key.
2026-09-07 23:46:53 +02:00
kylehgc 27abf4ad7f fix(diff): round 8 -- silent-loss and name-fidelity sweep across git, GNU diff, hg and svn
Review round 7 asked for two things; a producer enumeration and two
adversarial review passes answered for thirty. Every item below has a
real capture in the corpus (81 fixtures now: git 2.47/2.54, diffutils
3.10 and 3.12, Mercurial 7.0, Subversion 1.14, PowerShell) and a test
that fails without the fix.

Silent loss (a file or content line missing while the output looked
complete) -- each now returns `None`, byte-exact raw passthrough:

- GNU sorts its output, so a fact the parser cannot read (`File X is a
  fifo ...`, `Symbolic links ... differ`, a translated `Only in`) can
  come before the first `Only in` / `Binary files` / `Files` line, and
  `-q` streams carry no echo at all. The latch is now `dropped_line` --
  any column-0 line dropped as prose outside a message region -- and is
  judged once after the loop against `gnu_stream`, which GNU's fact arms
  set as well as its echo (hg's echo does not: an `hg log -p --template`
  prologue is prose, not a fact).
- `hg export`'s region stayed open until the first header pair and
  swallowed `Binary file bin.dat has changed` on a binary-first
  changeset. hg's `diff -r` echo (one `-r` from `hg diff`, two from
  `hg export` / `hg log -p`) now closes the region, counts as reaching
  its body, and the line is a binary fact; rule 6 stays live inside an
  hg region. `hg log -p`'s `changeset:` line opens the region like
  `# HG changeset patch` does.
- `git diff --word-diff` / `--color-words` hunks whose lines are all
  indented carry their `[-x-]{+y+}` markers behind the indent, so the
  hunk was booked as context and the file vanished. A hunk that closes
  with no `-`/`+` line is not a unified-diff shape (whitespace-ignoring
  modes suppress the hunk) -> `None`.
- `git format-patch --submodule=log` puts `Submodule sub a..b:` where a
  file section would go, inside the message region where rule 6 is
  suppressed. The strict `<hex>..<hex>[ (<how>)]:` shape is admitted
  there (`is_submodule_range`); prose never ends a line that way.
- A stream cut off right after `diff --git` / `index` dropped the file
  it named: git never emits a header-only section, so one that closes
  with nothing behind it -> `None` (`FileEntry::from_git`; `flush`
  returns `Option`). git's `index` / `similarity index` lines are
  structural and no longer count as dropped prose.
- A plain `svn diff` names a binary only as `Index: b.dat` + `Cannot
  display: file marked as a binary type.`, both prose before: the
  binary vanished beside its text siblings. `Index: <path>` opens a
  section (rule 3c); the notice is its binary note; an `Index:` section
  that closes empty is `(no content)` (a copy/move target) unless a
  column-0 line was dropped inside it (a localized notice) -> `None`.
- A binary section followed by another producer's header pair was
  renamed in place and its `binary` note landed on the next file; a
  pair after a binary section opens its own section.

A crash: the shared-tail scan behind `diff --git X Y` and `Binary files
X and Y` compared bytes, and two different multi-byte characters can
share trailing bytes (`😀`/`🙀`, `é`/`©`): the slice landed mid-character
and the process aborted with no output at all. The scan backs off to a
character boundary on both sides.

Names, byte-equal to the real filename under the producer's own root:

- `dequote` decodes git's C quoting (`"caf\303\251.txt"` -> `café.txt`)
  instead of stripping the wrapper; a name that would split a `[file]`
  line (newline) or is not UTF-8 keeps git's own quoted spelling, with
  the prefix stripped inside the quotes, so `rename to`, `diff --cc` and
  the header pair agree on one spelling and two Latin-1 names stay two.
- `unquote_shell` decodes diffutils >= 3.11's shell quoting on `Only in`
  / `Binary files` names -- `'…'`, `$'…'` and the `"…"` form it picks
  for a name holding a `'`; a quoted directory may itself hold `: `, so
  the `Only in` split follows the quoting (`shell_word_len`). 3.10
  prints those names bare; a bare `Only in` with more than one `: `
  splits at the root the stream already named on its echo, header pairs
  or `Binary files` lines. Both generations are in the corpus.
- `diff --git X Y` is split where the sides share the longest tail at a
  `/` boundary (`shared_tail`); the first path component before the
  tail on each side is that side's prefix (`FileEntry::prefixes`
  replaces `prefixed`): `a/`/`b/`, `i/`/`w/` under
  `diff.mnemonicPrefix`, `--src-prefix`/`--dst-prefix`, none under
  `--no-prefix`. The header pair strips exactly those, so `* Unmerged
  path b/inb.txt` folds into its `i/b/inb.txt w/b/inb.txt` section, a
  binary under a directory named `x b/` keeps its path, and `git diff
  --no-index d1/f d2/f` names `d2/f`. `diff --cc <path>` is kept whole;
  `--no-index --no-prefix x.bin y.bin` names `y.bin`.
- GNU diff names files under the roots it was given, never prefixes:
  header pairs after a GNU echo and the standalone `Binary files` /
  `Files` arms keep them (`diff -ru a b` -> `b/c.txt`, `diff -ru .
  ../new` -> `../new/x`), splitting `X and Y` by the same shared tail.
- Timestamps are stripped from `---`/`+++` lines only, at the LAST tab
  and only when shaped like one of the real producers' (GNU ISO date,
  hg `Mon Sep 07 …`, svn `(revision 1)`, git's bare tab), so a name
  containing a tab survives even under `hg diff --nodates`.
- An `* Unmerged path` fact git printed unquoted and cut at a newline
  folds into its quoted section instead of a phantom entry.

Spurious raw fallbacks on well-formed streams, now condensed: `git log
--stat -p` / `git show --stat -p` (bare `---` followed by a diffstat
line is the separator -- which also drops the old ` name | 3` bound),
`git log --numstat -p` (`-<TAB>-<TAB>` binary rows), `hg log -p` with
and without `--template`, an hg binary-only changeset whose message has
a marked non-prose line, `--format=%B` prose starting with `diff ` (the
GNU echo arm keys on `diff -`), `diff -u` on directories (`Common
subdirectories:` dropped), an svn copy/move target.

Also: GNU `-s` / `-q` facts are entries; a PowerShell-written stream's
UTF-8 BOM is stripped; a submodule both dirty and moved is one entry;
the budget replay in property (a) asserts under-consumption; the docs
list rule 1b and every bound is executable in `documented_bounds_hold`.
2026-09-07 15:56:00 -04:00
kylehgc 9634a9a82b fix(diff): round 7 -- no-newline marker placement, hg export region, dequoted renames
`\ No newline at end of file` describes the line directly above it. The
parser never renders context lines, so a marker git attached to an
unchanged last line landed under the last `-`/`+` line rendered and
claimed THAT line lacked a newline -- the one place the output stated
something untrue about the diff. The marker is now kept exactly when the
line above it was emitted (in-hunk after a `-`/`+`, or on the line right
after the budget closed on one), which keeps the witness of a
newline-only change and drops a marker that would describe the wrong
line. `emitted_at` replaces `closed_at`: the position test is the same
for both rules. Real fixture `git_diff_no_newline_raw.txt` carries both
shapes.

`# HG changeset patch` opens a message region like an mbox `From`, so
`hg export`'s column-0 headers and message no longer latch
`dropped_prologue` and send the whole changeset raw through its own
`diff -r <a> -r <b> <file>` echo. Real fixture `hg_export_raw.txt`
(Mercurial 7.0.1) joins the corpus.

`rename to`/`copy to` (and their `from`) go through `dequote`, so a
`core.quotepath` name has one spelling across a stream. The submodule
note keeps the range's direction qualifier:
`[file] sub (submodule e139196..b0ac9b1 rewind)`.

The body-line replay in `corpus_every_marked_body_line_survives` shares
the parser's per-parent presence rule (there is no second formula for
combined-diff columns); its doc now says so and names the oracle -- git's
header counts -- and it asserts a budget is never still owed at a
non-body line or at EOF, so under-consumption fails there too.
2026-09-05 19:44:03 -04:00
Ilia Alshanetsky d4239ecb90 fix(diff): align by LCS and name the cause of an invisible difference
develop now carries the byte-equality verdict (0cb34ac, 428af61), so this
branch no longer argues for it. The alignment fix and the reporting that
depends on it remain.

compute_diff compared line N to line N, so a single inserted or deleted
line reported every line after it as modified. This aligns by Myers' greedy
edit script, so one insertion reads as one insertion.

The script folds into hunks, one per run of differing lines with no
matched line between. Both renderers group by hunk: the classic fallback
prints one NcM per region, as diff does, because the classic format cannot
express which old line became which new line, and rendering the pairing
split a region into an NcM plus a trailing NaM that diff never prints.
Inside a hunk the condensed render pairs old and new lines by best
similarity rather than by position, so an insertion at the head of a run
no longer shifts every pairing after it into a rewrite that did not happen.

Op::Keep carries a run length. One entry per matched line made the script
scale with the file rather than with the change, 45MB of separators for a
million-line pair with 700 rewrites. The aligner's trace is one flat vector
with each round's bookkeeping stored inline, so MAX_TRACE_CELLS charges
what is allocated: a per-round Vec cost 5.8x the cells being counted on a
lopsided pair.

The bool develop passes into render_diff cannot express the third case the
aligner produces, a comparison that ran and then refused to build a listing
because it would cost more than the raw text. FileComparison replaces the
bool with Identical, InvisibleDifference and Lines, and DiffResult::unaligned
carries the refusal reason. An empty change list is not a synonym for
identical, and routing a refusal through the identical branch exits 0 on
files that differ.

The invisible-difference message keeps develop's wording, so the phrase
diff_byte_accuracy_test matches on survives, and then names the measured
cause instead of stopping at "no line-content change". It is shown whenever
the case arises. A fixed token allowance above raw could not be met: the
message's shortest form is ~20 tokens and a one-line pair ~2, so the ceiling
sat under the message's floor and dropped it on 90% of one-line pairs,
printing the two indistinguishable blobs it exists to replace.

The render drops the file-pair header and the blank line, names the files
in the frame legend by argument position instead of by path, and prints
the counts line only once the listing runs to a screenful. On 120
agent-sized diffs the framing cost more than the change list saved: the
body alone is 7% smaller than diff, and each line of chrome erased that.
2026-09-05 15:56:58 -04:00
Nicolas Le Cam e53ec1cf18 Merge pull request #3707 from alvins82/omp-shared
feat(omp)!: add Oh My Pi (OMP) support
2026-09-05 01:54:17 +02:00
Nicolas Le Cam 84f629d719 Merge pull request #3773 from KuSh/fix/golangci-lint-benchmark
fix(ci): unbreak the golangci-lint benchmark row
2026-09-04 18:50:43 +02:00
kylehgc d12acfcf93 Merge remote-tracking branch 'upstream/develop' into cla-fix 2026-09-03 18:20:42 -04:00
kylehgc 4ccb5aab3d fix(diff): round 5 -- translated GNU fact lines fall back raw, submodule range kept
GNU diff translates `Only in` and `Binary files … differ` (git translates
none of its own lines). Rule 6 read the English spelling only, so on a
French machine `diff -ru g1 g2 | rtk diff -` dropped the three translated
fact lines as prose and, because a text section still parsed, returned
`Some` -- four files became one, silently.

Rule 3b now reads GNU diff's per-file `diff <opts> X Y` echo (printed
whenever it compares directories) as the mark of a `diff -r` stream. Such
a stream carries no prose, so a column-0 line no arm read there is a fact
in a language the parser does not speak, and rule 9 returns `None`
instead of dropping it. A line dropped before the echo settled the
question counts the same way once it does -- `diff -r` may list an
`Only in` first. Real fixture `diff_ru_fr_raw.txt` (`LC_ALL=fr_FR.UTF-8`,
diffutils 3.10) pins the raw fallback; the English `diff_ru_raw.txt`
still parses in full, as do `git log -p` streams whose prologue is prose.

The `Submodule <path> <a>..<b>` note now carries the range --
`[file] sub (submodule e139196..b0ac9b1)` -- for parity with the dirty
arm's `(submodule, modified content)`.
2026-09-03 12:45:07 -04:00
Nicolas Le Cam 1173de0686 fix(js): correct bun and deno runtime behavior
Failure detection, so a green run is never reported red:

- a failure marker vouches for the block it closes, so a frameless failure
  such as a timeout keeps the line that says why it failed
- bun's marker and run footer are matched with the duration and test count
  they carry, since a test is free to log a line that starts the same way
- deno's FAILURES entries are matched by shape, because a bare " => " also
  occurs in arrow functions and assertion messages
- deno fences output it did not write, and names the fence for when it was
  printed, so the rule is matched by shape and gates both the failures list
  and the block engine

Diagnostics that were reaching nobody:

- deno's type-check errors are kept with the frame under them: they open no
  block and carry no section, so a type error left the caller with a bare
  "Type checking failed."
- bun's frames are kept, since two failing tests that share a name, and a
  module error that has no marker at all, cannot otherwise be located

Runs whose output rtk must not touch:

- watch mode on every filtered entry point goes through unfiltered, since the
  runners buffer the whole stream until a child that never exits
- a deno reporter the user named is left alone, because --reporter=junit and
  --junit-path write the report to stdout
- bun build and deno compile run unfiltered: without an output flag the bundle
  is stdout, with one the emitted-file summary is the point of the run, and on
  failure the diagnostics are, none of which an errors-only filter keeps

Tool resolution:

- a package runner the user named wins over lockfile detection, and over a
  tool that merely happens to be on PATH
- lockfile detection never resolves through bunx, which always fetches a
  missing tool and cannot be told not to
- a tool rtk may fetch resolves through npx, the only runner that can

Input and output:

- bunx forwards the caller's stdin when it is a pipe, since the filtered path
  buffers output and an inherited terminal would let a prompt wait invisibly
- strip the resolution lines bun actually prints when piped, count the tree
  levels drawn with a leading pipe, and stop injecting --json into a command
  the user typed
- the lint label records the arguments once, and the shell runners' placeholder
  tool name stays out of their verbose line

Fixtures are real bun 1.2.20 and deno 2.9.6 captures for each case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 02:16:01 +02:00
alvins82 d302797c51 fix(hooks)!: harden aliased Pi and OMP extension lifecycle
Treat pre-existing extensions without ownership state as uncertain, cover project-scope aliases, clean canonical sidecars after symlink removal, and classify protected extension overwrites as breaking.

BREAKING CHANGE: non-interactive installs of modified or unrelated Pi/OMP extensions now require --auto-patch to approve overwrites.
2026-09-03 11:04:19 +12:00
Geoff Little 950e772225 refactor(runner): one failure-block engine for bun and deno
- core/runner.rs: bun and deno share a buffered engine and differ only in an
  open, prove, close policy table, so a guard cannot land on one runtime alone
- core/runner.rs: deno gains the proof rule it never had, so a passing test
  that logs its own error line no longer reports as a failed suite
- core/runner.rs: a block closes when the next one opens, so consecutive
  stack-free failures stay separate
- tests: every scenario is asserted on both runtimes from real fixtures
2026-09-02 23:54:02 +02:00
Geoff Little 50bee30977 fix(runner): keep real diagnostics, drop test stdout
- core/runner.rs: bun error blocks are kept only with proof bun wrote them
- core/runner.rs: emit diagnostics when no failure marker was printed
- core/runner.rs: deno section boundaries close an open failure block
- tests: real bun 1.3.6 and deno 2.9.6 fixtures for all three cases
2026-09-02 23:53:21 +02:00
Geoff Little 65cd2f7851 fix(runner): parse current deno test output format
- deno 2.x prints FAILURES section and 'FAILED | N passed' footer,
  old anchors (test result:, failures:) never fired
- capture ERRORS diagnostics, strip ANSI before anchoring
- TestEcosystem enum picked once at boundary, match replaces
  co-firable substring flags (cargo test contains go test)
- real deno 2.9.1 fixtures with golden and savings tests
2026-09-02 23:53:21 +02:00
Geoff Little 4fb2c3abe1 fix(bun): parse real pm ls tree output
- bun 1.x ignores --json for pm ls, JSON path never fired
- bun_cmd.rs: tree parser for glyph rows, sorted and deduped
- parser choice sniffs printed content, not the flag passed
- real bun 1.3.6 fixture replaces synthetic-JSON-only coverage
2026-09-02 23:53:21 +02:00
Geoff Little 9e2e4b6793 fix(runner): anchor bun test summary extraction
- runner.rs: anchor bun count lines, stop echoed source leak
- capture error and Expected/Received detail per failure
- add real bun 1.3.6 fixtures with golden and savings tests
2026-09-02 23:53:21 +02:00
alvins82 ba6a1f57a3 fix: harden shared extension ownership handling 2026-09-03 09:35:36 +12:00
kylehgc 613e78558d fix(diff): round 4 -- fact-arm names, dirty submodules, no-prefix renames, quoted paths
Review round 3 on the file-level-fact arms, plus a cold pass on top:

- `Only in <dir>: <file>` splits on the FIRST `: ` (GNU diff's separator
  follows the directory; a filename may carry its own).
- Standalone `Binary files X and Y differ` splits at the ` and ` where
  both sides agree past their first component, so a filename containing
  ` and ` is not cut, and is named through `header_name` so the `a/`/`b/`
  strip follows the same decision as a header pair.
- `* Unmerged path X` folds into the section git emits for X right after
  it: one `[file] X (unmerged) (+A -B)` entry instead of two.
- `Submodule <path> <a>..<b>` keeps only the path in the name slot (the
  tokens before the LAST `..` token, so a path containing `..` survives),
  and the sha-less `Submodule <path> contains ... content` dirty line is a
  fact too -- it used to be dropped silently beside a condensed sibling.
  Real fixture `git_diff_submodule_dirty_raw.txt` joins the corpus.
- `rename to` / `copy to` is git's exact path; the `+++` pair no longer
  re-derives it, which stripped a real `b/` directory under `--no-prefix`.
- `core.quotepath` wraps non-ASCII paths in quotes on every header line;
  names are dequoted before the prefix strip.
- The header-pair hunk gate applies to open hunkless sections as well:
  git only emits `---`/`+++` before the first `@@`, so a stray pair there
  is lost content and falls back raw instead of renaming the section.
- `diff --cc X` carries one path and leaves `prefixed` to its header pair.
- `past_diffstat` comment states the first-section-mangled shape it alone
  catches; the ` name | 3` bound says it fires always (the `---` separator
  is the marked line that follows); the stdin doc says the never-worse
  check is inlined and why.

A/B against 9106d48: 400 `git show` streams byte-identical; 290
format-patch series identical except 12 that fell back raw through a
prose pipe and now condense, zero new fallbacks.
2026-09-01 22:07:55 -04:00
kylehgc 9106d4897b Merge remote-tracking branch 'upstream/develop' into cla-fix 2026-09-01 17:46:41 -04:00
kylehgc 120450b480 fix(diff): round 3 -- content bytes through ANSI, mbox diffstat gate, --no-prefix names
Structure is read through a per-line ANSI/CR-stripped view and content is
pushed raw, so escapes that are part of the user's content survive (the
file path was already byte-faithful on the same input). Rule 8's mbox
tolerance now ends at the first diffstat line: an orphaned hunk body in a
hand-edited series falls back raw instead of being dropped as prose, while
version notes between `---` and the diffstat stay prose. `diff --git X Y`
with X == Y marks a --no-prefix stream whose header names keep their
leading a/ b/ (creations and deletions included); the check is confined
to the two-path form. The new-side `\ No newline` marker is kept only on
the line right after its hunk closes. The dead `!in_prologue` conjunct
and the variable behind it are gone.

Real `git diff --cached --no-prefix` fixture added to the corpus.
2026-09-01 17:46:41 -04:00
Ben Younes 0cb34acccb fix(diff): don't report byte-different files as identical
rtk diff compared files via str::lines(), which strips a trailing \r and
drops a trailing newline. CRLF-vs-LF and trailing-newline-only differences
therefore produced an empty line-diff and were reported as
"[ok] Files are identical" with exit 0 — a false-equal result that defeats
rtk diff used to verify two files are equal.

Guard the identical branch with a byte comparison: only claim identical when
the bytes truly match; otherwise surface the whitespace/line-ending
difference and exit 1 per the diff convention.

Fixes #3469

Co-authored-by: Luca "Hydr0gen" Vitale <78725363+Hydr0gen19@users.noreply.github.com>
2026-09-01 21:03:03 +00:00
Nicolas Le Cam eb3f814872 refactor(grep): trim comments and align the siblings of the flag change
Condense the Grep variant and its tests to the comment rules in
CONTRIBUTING.md (explain why, not what), and carry the -l/-t change
through the files that still assumed the old behavior.

- scripts/test-aristote.sh: add -r to the two grep assertions, since
  `grep PATTERN <dir>` exits 2, and move the type filter to `rtk rg`,
  the engine where -t is valid.
- docs/usage/FEATURES.md: extra args go to the engine actually invoked,
  not always to rg; drop the per-row shortcut justifications.
- tests/search_compress_test.rs: guard grep-engine tests on
  grep_available() instead of rg_available(), which skipped them into a
  false green on a box without ripgrep.
- .claude/hooks/rtk-suggest.sh: reverted; the rg -> rtk rg fix is
  correct but belongs in its own PR.

BREAKING CHANGE: `rtk grep --file-type` and its `-t` short are removed.
The option never reached the engine, so it was a silent no-op; `-t` now
flows through, which means `rtk rg -t rust` filters by type while
`rtk grep -t rust` returns grep's own `invalid option -- 't'`. `-l` and
`-m` under `rtk grep` are likewise the native grep flags now rather than
rtk's --max-len and --max.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 01:58:35 +02:00
kylehgc eb088f2f65 fix(diff): preclear round 3 -- keep the no-newline marker, strict UTF-8 gate
- '\ No newline at end of file' survives to the output (in-hunk and the
  common new-side position just after the budget closes): it is the only
  witness of a trailing-newline-only change, which otherwise renders as
  two byte-identical -/+ lines. Real fixture; proven load-bearing by
  revert.
- Non-UTF-8 stdin now always takes the raw-bytes branch, even when the
  stream parses: condensing would rewrite content bytes to U+FFFD, and
  byte fidelity outranks savings. (Base hard-errored here; raw
  passthrough is strictly better on both counts.)
- Doc-comment bounds recorded for the mbox header-pair spoof and the
  raw-fallback newline append.
2026-08-30 22:29:17 +00:00
kylehgc 7689020ca5 fix(diff): preclear round 2 -- file-level facts, sha256 mbox, contexts
- File-level facts outside hunks become note-only entries instead of
  vanishing while the stream condenses: 'Only in <dir>: <file>' and
  standalone 'Binary files ... differ' (GNU diff -r), '* Unmerged path'
  (git diff --ours during a merge), and 'Submodule <a>..<b>' headers.
  Suppressed inside mbox message regions, where column-0 prose is
  indistinguishable by value. Real fixtures captured for all four shapes;
  fix proven load-bearing by revert.
- is_mbox_from accepts 64-hex separators: SHA-256 repos' format-patch
  streams no longer fall back raw wholesale.
- .context() on the stdin read and raw-fallback write paths.
- Savings comment corrected to the measured corpus numbers (52-87%
  metadata-heavy, single digits content-heavy); exemption escalation
  unchanged.
2026-08-30 22:29:17 +00:00
kylehgc 970b3b20dc fix(diff): preclear round 1 -- report hunkless empty-file sections, tighten guards
- new file mode / deleted file mode sections (an empty file added or
  removed) get their own notes; previously they vanished silently whenever
  another file in the stream parsed cleanly. Real fixture captured, fix
  proven load-bearing by revert.
- Signature tolerance (rule 6) now requires the stream to have carried an
  mbox From separator; a bare '--' in a plain diff falls back raw.
- never_worse discrimination by token estimate instead of pointer
  identity; stdin decoded once (Cow) instead of twice; hunk-body prefix
  checked on the byte view without a per-line allocation.
2026-08-30 22:29:17 +00:00
kylehgc f42eaf92bd fix(diff): review round 1 -- byte-raw fallback, strict prefix width, fixture hygiene
- Structural fallback now emits the caller's exact bytes (a lossy UTF-8
  decode or ANSI-stripped 'raw' is not raw); the decode-strip-parse-guard
  pipeline moved into condense_stdin so it is unit-testable.
- Dropped the mailer-padding tolerance: a line shorter than the hunk's
  prefix width now falls back instead of being guessed into context.
- Plain-diff fixtures recaptured with relative paths (no local usernames
  or session ids); copy-only and mode-only sections get real fixtures and
  a vanish-class test.
2026-08-30 22:29:17 +00:00
kylehgc ee614e4c63 fix(diff): region parser for condense_unified_diff -- budget-owned hunks, raw fallback
Parse the stream into (prologue)(file-header)(hunk)* regions before
classifying: hunk extent comes from the @@ line budget (one old-budget per
parent, so @@@ combined headers parse too), content is classified only
inside hunks, and any structural disagreement (invalid body prefix, budget
over/under-run, budget owed at EOF, malformed @@) falls back to raw
passthrough. Detector precedence is a documented total order on
condense_unified_diff_strict.

Fixes, each with its reproducer from
claudedocs/diff-classifier-review-2026-08-29.md as a test:
- SQL/Lua '-- comment' removals (--- on the wire) no longer dropped
- added '++ text' lines no longer parsed as file headers
- format-patch mbox prose and the '-- ' signature no longer counted
- +++ /dev/null deletions named by their --- side
- binary / rename-only / mode-only sections reported instead of vanishing
- --color input stripped (was: silently empty output), non-UTF-8 stdin
  read lossily (was: hard error, no fallback)
- CRLF content bytes survive verbatim (split('\n'), not lines())
- b/ prefix stripped exactly once; timestamps tab-split from header names
- the '... +N more' trailer removed: it claimed truncation that never
  happened (all lines were printed above it)

Fixture corpus: 13 real-producer captures (git 2.54, GNU diff) in
tests/fixtures/diff -- multi-file, -U0, -W, rename/delete/binary,
log -p, show --cc, format-patch single/series/cover, diff -u/-ru/-rN,
CRLF. Property tests: every marked body line survives to the output,
counters equal rendered lines, the fallback fires on zero corpus fixtures.
2026-08-30 22:29:17 +00:00
Brandon Harper cd0c7bc17f test(grep): restore the -l end-to-end test, with a numeric pattern
I deleted this test earlier on the reasoning that `-l` printed the same
thing with and without the fix. That reasoning was wrong, and so was the
mutant I used to "prove" it -- a word pattern.

The behavior splits on whether the PATTERN parses as a usize:

- Non-numeric ("TODO"): clap rejects, run_fallback re-runs raw grep, output
  and exit code are identical either way. This is the case I tested, which
  is why the mutant survived.
- Numeric: clap ACCEPTS. `-l 8080 hit.txt miss.txt` sets max_len=8080 and
  leaves hit.txt as the pattern, miss.txt as the only path. No error, no
  fallback -- just an empty result and exit 1 on a file that does match.

numeric_pattern_with_files_with_matches_flag pins it. Mutation-proved by
restoring `short = 'l'`:

    `grep -l <number>` must find the match (#2628); status=Some(1) stderr=
    test result: FAILED. 0 passed; 1 failed

Single-file form is worse still -- the filename becomes the pattern, no
path is left, and rtk falls back to stdin:

    # mutant, one file
    $ rtk grep -l 8080 one.txt < /dev/null      -> exit=1, no output
    $ rtk grep -l 8080 one.txt                  -> hangs on a terminal
    # fixed
    $ rtk grep -l 8080 one.txt                  -> one.txt, exit=0

Corrects the field comment, which repeated the wrong story ("could not
parse at all", "losing all compaction"). Nothing is lost to compaction:
`-l` is a has_format_flag passthrough on the fixed path too, so there are
no savings for this flag either way. The cost was a wrong answer.

Also repoints the last stale module reference. test_grep_version_routes_to
_extra_args said `grep_cmd::run forwards it to rg --version`; grep_cmd.rs
does not exist and the Grep arm routes to search::run, whose
--version/--help check execs against the engine unfiltered. `grep -rn
grep_cmd src/ tests/ docs/` is now empty.
2026-08-30 11:22:22 -06:00
Brandon Harper d5cb3b1abb test(grep): close three gaps a mutation audit found
1. The --file-type removal had ZERO coverage. Restoring the option long-only
   (no `-t`) left the entire suite green, so nothing stopped it creeping
   back. Added test_grep_file_type_option_is_gone, which pins that the flag
   routes to extra_args. Note it is NOT a clap error -- unknown long flags
   fall through to the trailing slot -- so an is_err() assertion would be
   wrong; grep is what rejects it. Mutation-proved:

       # --file-type restored long-only (+ file_type: _ so it compiles)
       assertion `left == right` failed
         left: ["FOO", "src/"]
        right: ["--file-type", "rust", "FOO", "src/"]
       test result: FAILED. 0 passed; 1 failed

2. max_len_long_option_still_binds_to_rtk guarded on rg_available(), but
   `rtk grep` execs grep, never rg (search.rs: `Engine::Grep => "grep"`).
   On a box without ripgrep the test skipped and reported success, hiding
   the mutant it exists to catch. Added grep_available() and switched to it.
   Proved with the call site discarding max_len and rg off PATH:

       $ PATH=/usr/bin:/bin search_compress_test max_len_long_option
       --max-len must still bind after the short form was removed
       (narrow=83, default=83)
       test result: FAILED. 0 passed; 1 failed

   Before this change that invocation printed `ok`.

3. test_grep_options_after_pattern_stay_in_extra_args claimed to
   demonstrate a `trailing_var_arg` gotcha. Removing trailing_var_arg leaves
   it green -- `allow_hyphen_values` is what produces the behavior. Comment
   corrected so it is not read as a guard on trailing_var_arg.

Also drops test_grep_parse_max_count_m_forwarded: it duplicates the
pre-existing test_try_parse_grep_dash_m_is_max_count (src/main.rs:3012),
which dies to the same mutant and asserts strictly more (it also pins
`max == 200`).

Walks back the Grep NOTE block, which overclaimed. It said this "completes"
the shadowed-short cleanup. It does not:
  - clap's auto `-h` still beats grep/rg's --no-filename before the first
    positional, and after it reaches search.rs's help check and execs
    unfiltered. That is #2532's scope, so the comment now says so rather
    than inviting the next reader to add disable_help_flag here.
  - removing a clap short does not make the letter safe end-to-end.
    search.rs shares one VALUE_FLAGS_SHORT table across both engines, so a
    letter taking a value in rg but not grep still eats the next token in a
    cluster: `rtk grep -rt FOO .` consumes FOO as -t's value no matter what
    clap does. Noted as a search.rs defect, not fixed here.

Separately, .claude/hooks/rtk-suggest.sh mapped both `rg` and `grep` to
`rtk grep`, swapping engines. With --file-type gone, its suggestion for
`rg -t rust FOO` became `rtk grep -t rust FOO`, which grep rejects. Now
maps rg -> `rtk rg` and grep -> `rtk grep`.
2026-08-30 11:22:22 -06:00
Brandon Harper 87bdb775dc test(grep): cover --max-len end-to-end after the short-form removal
Adds max_len_long_option_still_binds_to_rtk: the surviving long form must
still reach rtk's truncation now that `-l` is gone.

Mutation-proved. Discarding max_len at the call site (`max_len: _`, so the
mutant compiles) turns it red:

    --max-len must still bind after the short form was removed
    (narrow=83, default=83)

Restored, it passes (narrow=43, default=83).

Two things the test has to get right, both found by watching it fail:
- It needs a bulky match set. A small result passes through uncompressed
  under the never-worse-than-plain guard, so a one-line file exercises the
  passthrough and proves nothing.
- It measures matched lines only. rtk's "+N more [see remaining: ...]"
  trailer is fixed-width chrome that --max-len does not govern, and it is
  the longest line in either run.

No companion `-l` integration test: `-l` mis-binding never changed program
output. A clap parse error routes through run_fallback, which re-executes
the raw command, and the fixed path sends `-l` to the has_format_flag
passthrough -- so both sides print the same file list and exit 0. A test
asserting that would pass with or without the fix. The clap-layer test
test_grep_parse_files_with_matches_l is the real gate there; it fails when
the `-l` short is restored.
2026-08-30 11:22:22 -06:00
Nicolas Le Cam 67007fe910 test(go): use captured golangci-lint output and drop the synthetic fixture
golangci_v2_json.txt is pretty-printed across 144 lines; golangci-lint emits its
JSON on a single line, so the fixture was a shape real output never takes. The
savings assertion counted whitespace-separated words, so the indentation was
doing the work -- against real single-line output that same helper reads 40
"words" for 4 KB and reports 30% for a filter that is doing its job. Measured
with estimate_tokens, the estimator RTK bills with, the real figure is 95.3%.

Replaced with output captured from golangci-lint in golang:1.27: a clean v2.13.2
report, a v2.13.2 report carrying 3 errcheck and 3 ineffassign findings, and the
628 bytes v1.64.8 writes to stderr when its vendored go/types cannot read Go
1.27 export data.

That last one backs a test the suite had no equivalent of: whatever the filter
is handed -- empty, whitespace, error text, truncated JSON, v1 or v2 -- it must
say something. Returning an empty string is what let `rtk golangci-lint run`
print nothing at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 14:58:55 +02:00
Nicolas Le Cam 774465e379 Merge pull request #3199 from kylehgc/feat/mvnd-support-3184-upstream
feat(mvn): add rtk mvnd support for Maven Daemon
2026-08-30 11:59:42 +02:00
Nicolas Le Cam c75522e200 Merge pull request #2250 from jsiu93/fix/ctest-filter-2237
feat(ctest): add compact output filter
2026-08-30 02:38:46 +02:00
Nicolas Le Cam 143dc8cf23 fix(ctest): validate the summary, keep reasons and trailers intact, size the caps
Seven limits, each reached with real ctest 4.3.4 output.

Result lines are validated against the run total, but the summary was not, and
`has_failures` trusts a summary over the parsed records. A test that forwards a
nested ctest run echoes that run's summary too, so a run killed before printing
its own reported the nested one: a SIGKILLed suite of 6 whose first test had
already failed rendered "ctest: 3/3 passed" off the forwarded 3-test summary.
CTest prints its summary once every test has finished, so a validated Start
behind a summary places that summary inside a test's output -- that identifies a
forwarded suite whatever its size, where a size bound alone only catches a
nested suite larger than the run. Two bounds cover a kill landing between the
forwarded summary and the next Start: the run total caps a genuine total, and a
summary cannot report fewer failures than the result lines already validated.

split_status_reason undid the fold's newline before a closing bracket with a
global replace of " ]", which also rewrites brackets belonging to the reason.
A real PASS_REGULAR_EXPRESSION of "zzz[0-9 ]end" rendered as Regex=[zzz[0-9]end]:
a character class silently loses the space it matches on, and the reader debugs
a pattern ctest never ran. Only the fold's own trailing " ]" is undone now.

--no-tests=error exits 8 and says "Errors while running CTest", but the empty
run rendered as a bare "ctest: no tests found" -- the one line that reads as a
benign outcome for a run that failed. The lines behind the no-tests marker are
kept, as they already are behind a failure.

-T Test is the canonical CI invocation and prints ordinary test output, yet it
bypassed the filter along with every other dashboard mode, so the most common
form saved nothing. The action decides now: Test stays filtered, every other
action still bypasses (-T Coverage opens with "Performing coverage"), and a
test model with no action to pair it with is left alone rather than guessed at.

looks_like_ctest_output required "Test project " to be the first non-empty line,
tolerating one specific preamble. `ctest -T Test` emits a repeated "Cannot find
file: .../DartConfiguration.tcl" ahead of the banner, so `ctest -T Test 2>&1 |
rtk pipe` fell through to identity. The banner is looked up within a short
leading window; a result or no-tests line must still follow it, which is what
keeps unrelated output out.

The failed list capped entries at the flat CAP_LIST while each entry renders a
header plus up to MAX_FAILURE_LINES detail lines, which is the multi-line case
core/README.md ("Deviating from a cap") reserves reduced() for. A 25-failure run
with 30 diagnostic lines apiece rendered 247 lines, 67.9% off the raw output --
above the floor but not compact. At reduced(CAP_LIST, 5) it renders 187 lines,
75.8% off, and the section tee still recovers every entry. The skipped and
raw-trailer lists keep the full cap: their entries are one line each.

Tracking bucketed ctest under "system" beside ls and grep, while discover
classifies it as a test runner; every other runner is bucketed by ecosystem.
2026-08-30 02:27:14 +02:00
xiaozh acbde6e443 fix(ctest): take the run total from the first result line and keep unparsed failures
Review round 6 of #2250.

- The run total used to validate result lines is the counter of the
  first result line that carries one. ctest prints its own result line
  before any output a test could forward, so that total identifies the
  run under `--stop-on-failure` (scheduled > ran), with disabled tests,
  under `-j`, and in a killed run whose test forwards a larger nested
  suite. The summary-based reconciliation and the majority vote are
  gone; `--stop-on-failure` details are back.
  Real capture: tests/fixtures/ctest_stop_on_failure_raw.txt.
- Wrapped result lines are folded until their terminator instead of
  giving up after eight physical lines, so a long `PASS_REGULAR_EXPRESSION`
  list no longer drops the test and swallows its neighbour's block.
  Real capture: tests/fixtures/ctest_long_regex_list_raw.txt.
- When the summary reports more failures than were parsed, the raw
  `The following tests FAILED:` entries are rendered under
  `failed (unparsed, raw):` (capped at CAP_LIST with a tee tail hint),
  so a parsing gap degrades the output instead of erasing the names.
- Dashboard long flags (`--dashboard`, `--test-model`, `--test-action`,
  `--script`, `--script-new-process`) bypass the filter like the short
  forms.
2026-08-30 06:25:59 +08:00
kylehgc 9e7e01d7dd fix(mvn): second review round -- root-lane disarm, per-lane summary tails, opener narrowing, drop insta
Review response for upstream #3199 (KuSh round 2), plus preclear findings.

- Root-lane claims disarm on any fall-through line again (pre-d602a3b
  behavior restored; tagged lanes keep the keyed-only rule). Fixes the
  plain-mvn frame leak on the repo's own fixtures -- filter_compile output
  is byte-identical to base ba7a9ce on mvn_test_fail_slice_raw.txt and
  mvn_test_multifail_slice_raw.txt, pinned by fixture-backed tests. Full
  base A/B: 56/56 outputs identical across all four entry points.
- Failures-summary tails are attributed per lane: dropped moves onto
  SurefireLane, each lane's '+N more failures' tail flushes at that lane's
  own aggregate (or at end-of-stream for truncated input), while the
  reactor-wide entry budget stays shared. A module's tail can no longer be
  collected under another module's header.
- is_lane_opener's [ERROR] arm narrowed to genuine compiler diagnostics
  (FILE_COORD), so [tag] [ERROR] app logs no longer escape a buffered
  block or get re-ordered ahead of it; they route by ownership rules.
- The [ERROR] arm sites only rewrite keep_continuation on the lane's own
  keyed lines, matching every sibling arm/disarm site; interloper sweeps
  now cover both [WARNING] and [ERROR] spellings on all three filters.
- insta removed (first dev-dependency, and .claude/rules/cli-testing.md
  prescribes plain assert_eq! with include_str! fixtures): the four
  snapshots are now expected-output fixtures under tests/fixtures/,
  Cargo.lock drops insta/console/encode_unicode/similar.
- split_lane's no-trailing-space tagged blank pinned by tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 15:39:26 -04:00
xiaozh ffe1d453c2 fix(ctest): validate result lines by run total and bypass dashboard modes
Review round 5 of #2250.

- Result lines are trusted only when their `N/M` total matches the run.
  The run total is the final summary total, reconciled for disabled
  tests (ctest counts them in `N/M` but not in the summary), or the most
  frequent counter when no summary exists. A test that forwards a nested
  ctest run can no longer replace a real record, truncate its own block
  or inflate the totals; dedup keys on number and name so `--repeat`
  retries still collapse. Framing uses the retained result lines only.
  Real capture: tests/fixtures/ctest_nested_result_raw.txt.
- Dashboard client modes (`-D`, `-D<x>`, `-M`, `-T`, `-S`, `-SP`,
  `--build-and-test`) bypass the filter like `-V` does, so diagnostics
  such as a missing DartConfiguration.tcl are shown verbatim.
- Folded result-shaped logical lines are split back into physical lines
  inside failure blocks, so each line is indented and counted against
  the block cap.
2026-08-29 15:38:59 +08:00
alvins82 157d5c2205 fix: close ownership lifecycle review gaps 2026-08-29 15:28:09 +12:00
xiaozh 6cf56a9c83 fix(ctest): validate framing lines and tee the failed section once
Review round 4 of #2250.

- Failure blocks are bounded by validated CTest framing instead of any
  line shaped like one. `Start N: name` counts only when a parsed test
  has that number and name; the summary, total-time and "The following
  tests FAILED:" lines count only at their last occurrence. A test that
  prints `    Start 3: inner phase` or a nested summary keeps its
  assertion evidence, and the outer summary stays authoritative.
  Real capture: tests/fixtures/ctest_spoofed_framing_raw.txt.
- Collection and rendering of failure blocks are split. Blocks are
  collected untruncated for every failed test, the section is rendered
  with the existing entry and head+tail caps, and one section-level
  `ctest-failed` tee holds the complete untruncated section whenever a
  block or the list was cut. The per-test `ctest-failure` writes are
  gone, so the hint no longer points at a colliding file (#3431) and the
  `[full output:]` file contains no `... +N more lines` markers.
- The `***Failed  <reason>` line stays outside the per-block cap.
2026-08-29 10:23:37 +08:00
xiaozh 6232c377e8 fix(tsc): count global diagnostics and keep the failure head
Review round 2 of #2232.

- Diagnostics without a file prefix (`error TS5023:`, `error TS5058:`,
  `error TS2688:`) match a third pattern, so they are counted, emitted as
  blocks on the streaming path and grouped under `global (N errors)` on
  the `rtk pipe --filter tsc` path. Output made only of global
  diagnostics no longer reports "TypeScript compilation completed"
  (#3220); the summary reads `TypeScript: N errors` when no file-scoped
  diagnostic exists.
- `parse_diagnostic` returns a borrowed `Diagnostic` instead of raw
  captures indexed by position.
- The exit-non-zero/no-diagnostics branch keeps the first and the last
  `reduced(CAP_WARNINGS, 5)` non-empty lines instead of a plain tail: tsc
  and npx print the cause first and boilerplate after it. Only retained
  lines are ANSI-stripped, and the pipe path borrows plain input instead
  of copying it.
- `--pretty` tests use a real TypeScript 6.0.3 capture and state that the
  code frame after the blank line is dropped on purpose. New fixtures
  cover a global config error and bare `tsc` without a project.
2026-08-29 10:19:51 +08:00
xiaozh 87702bf339 fix(ctest): address review round 3
- summary line is authoritative for pass/fail routing and counts; a parsed
  failure no longer overrides "0 tests failed" (--repeat until-pass)
- recognise retry result lines without the N/M prefix; the last result per
  test number replaces the earlier one in place
- pre-result diagnostics are only claimed when the nearest boundary is a
  Start line, so -j runs no longer absorb other tests' result lines
- failed entries are labeled "#N name (status, duration)" with their details
  indented beneath; empty blocks can no longer shift attribution
- failed: and skipped: lists are capped at CAP_LIST with tee recovery; the
  duplicated CTest FAILED name list is dropped, its trailer lines are kept
- failure detail keeps 2 head lines + 8 tail lines so pre-result diagnostics
  such as "Could not find executable" survive truncation
- fixtures re-captured as stdout followed by stderr, matching runner.rs; add
  real -j4 and --repeat until-pass:2 captures

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 10:06:57 +08:00
xiaozh 4147e99aa3 fix(ctest): address review round 2
- migrate regex statics to std::sync::LazyLock (lazy_static left develop in 5269df7)
- use ..RtkRule::DEFAULT for the ctest rule (new pipeline_final_safe field)
- join wrapped `***Failed  <reason>` result lines before parsing and surface the reason
- give ***Skipped tests their own bucket instead of reporting them as failures
- report "no tests found" only for the literal `No tests were found!!!` line when nothing parsed
- cap per-failure output at CAP_WARNINGS (tail) and the FAILED list at CAP_LIST, with tee hints
- add real ctest 3.25.2 fixtures with exact-output and >=60% savings tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 10:06:57 +08:00
alvins82 8c25ada388 fix: harden shared extension ownership 2026-08-29 13:22:28 +12:00
Nicolas Le Cam d9a589389d Merge pull request #3259 from breisnerlopez/fix/grep-m-max-count-collision
fix(grep): free -m for GNU --max-count instead of rtk --max
2026-08-29 01:34:20 +02:00