Skip to content

Next Release - #4228

Merged
aeppling merged 102 commits into
masterfrom
develop
Oct 2, 2026
Merged

aeppling merged 102 commits into
masterfrom
develop

Conversation

@rtk-release-bot

@rtk-release-bot rtk-release-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Feats

  • feat(hook): add native Google Antigravity plugin lifecycle and hook support #3711 — Closes #1975 (to verify)
  • feat(search): fold shared path prefix in file-list passthrough #4170

Fix

Other

  • ci(transparency): trigger transparency analyze #4371
  • chore(deps): bump actions/checkout from 4 to 7 #4353
  • bench: measure both sides of every row on stdout+stderr #3774
  • test: stop tests depending on the contributor's locale and repo #3826
  • build(deps): bump actions/setup-go from 5 to 7 #3819
  • build(deps): bump actions/stale from 9 to 11 #3821
  • build(deps): bump actions/github-script from 7.1.0 to 9.0.0 #3823
  • build(deps): bump softprops/action-gh-release from 2 to 3 #3816
  • chore(deps): bump actions/upload-artifact from 4 to 7 #3815
  • refactor(hooks): split init.rs into per-agent submodules #3878
  • refactor(hooks): consolidate hook config I/O #4151 — Closes #4002 (to verify)

hgunduzoglu and others added 30 commits June 11, 2026 19:04
MinimalFilter used trimmed.contains() to detect block-comment openers,
so any code line containing /* as a substring (string literals, globs
like "src/*.rs", regex patterns) was dropped — and when no */ followed
on the same line, in_block_comment leaked and silently swallowed the
rest of the file. Match openers with starts_with() so only genuine
comment lines enter block-comment state.

Fixes #2385
rtk init -g --agent cursor fell through to the shared init path with
install_claude derived only from !opencode, so it created RTK.md,
CLAUDE.md, and patched settings.json under ~/.claude in addition to
patching .cursor/hooks.json. On machines without ~/.claude it failed
outright when creating the temp file.

Two fixes:
- Gate install_claude on the absence of a sibling agent target (Cursor,
  Windsurf, Cline) so only the default or explicit Claude target
  installs Claude Code files.
- Create ~/.cursor before patching hooks.json; atomic_write places its
  temp file in the target's parent, which failed on a fresh machine
  where the directory did not yet exist.

Closes #2097
Python has no block comments, but the minimal filter treats """ as one.
Two things go wrong.

A line like `QUERY = """` contains the delimiter without starting with it,
so it is taken for a block-comment opener. The same """ then satisfies the
closing test on that one line, so the line is dropped and its string body is
left behind as loose text.

A single-line docstring such as """Module doc.""" toggles the docstring flag
once and never back, so the filter stays in docstring mode for the rest of
the file and stops stripping comments entirely.

Give Python a string-aware path instead. Minimal keeps docstrings, so the
only thing to remove is # comments and the only state needed is whether we
are inside a triple-quoted string. Delimiter kinds are tracked separately so
a ''' inside a """ string does not close it.

Other languages keep the existing block-comment walk untouched.

Refs #3260
`rtk discover -p <filter>` matches the filter against project directory
names with a case-sensitive `contains`, so on Windows it can come back
empty even when the project is there. NTFS is case-insensitive and Claude
Code builds the folder name from whatever cwd casing it was launched
with, so one project can end up under both `E--proj` and `e--proj` while
`std::env::current_dir()` hands back an uppercase drive letter.

Compare case-insensitively under `cfg!(windows)`. Unix behavior is
unchanged.

Refs #2919
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 7.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](actions/setup-go@v5...v7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/github-script](https://github.com/actions/github-script) from 7.1.0 to 9.0.0.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@f28e40c...3a2844b)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 11.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](actions/stale@v9...v11)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '11'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
`format!("Failed to run {} pip list", base_cmd)` was written for the
`uv pip` branch, so on the plain-pip path it printed "pip pip list".
Add `prog_label()` returning "uv pip" or "pip" and use it in all six
verbose/error strings. The executed argv was already correct.

Fixes #4050
`test_git_status_not_a_repo_exits_nonzero` matched git's English "not a git
repository" on stderr, so it failed in any other language -- `git status` is a
passthrough and keeps the user's locale by design. It now asserts what RTK
actually owns there: a non-zero exit, the message on stderr, nothing on stdout.

The quieter half: `!out.contains("Is a directory")` in search_faithful_test
passed vacuously wherever grep speaks another language, testing nothing. The
engine-spawning helpers pin LC_ALL for both sides of a comparison, so those
assertions mean the same thing everywhere.

The branch tests ran `git branch` in whatever repo the contributor was sitting
in and deleted it afterwards; the broken-pipe test read that repo's log, and
outside a repo it stopped exercising the pipe at all. All three take a
throwaway repo from the new test_support helpers, which also neutralize
GIT_CONFIG_GLOBAL/SYSTEM so local aliases and init.defaultBranch stay out.

Verified against fr_FR, de_DE, an empty git config and an injected
RIPGREP_CONFIG_PATH.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Its match_command, `^(gradle|gradlew|\./)gradlew?\b`, needs a doubled
token (`gradlegradle`) or a literal `./gradle…`. Both call sites reduce
argv[0] to its basename before the lookup — `run_fallback` in main.rs and
`strip_absolute_path` on the hook rewrite path — so the `./` branch is
unreachable too. The filter has matched nothing since it landed: the
basename logic arrived in adda253 (2026-03-10), the file in 8fae5b0
(2026-03-18).

Deleting it changes no output. `src/discover/rules.rs` already routes the
whole gradle family (`gradle`, `gradlew`, `./gradlew`, `gradlew.bat`) to
`rtk gradlew` and the Rust gradlew_cmd, and `gradle … bootRun` is filtered
by spring-boot.toml. Widening the regex instead would have shadowed
spring-boot.toml — first match wins, ordered by filter name — and pulled
gradle-profiler and gradle.bat into a head-truncating filter.

Making TOML filters reachable from the default passthrough is a separate
design question, tracked in #3904.

Refs #2708
`rtk grep -l`, `grep -L`, `rg -l`, and `rg --files` print every path in
full and record 0/0 savings. Agents search from absolute roots, so each
line repeats the same long directory prefix.

Fold that prefix into a `<prefix> (N files)` header and print each
path's tail. The transform is lossless, keeps engine order, cuts only on
whole components, and runs under `never_worse`. It applies only to a
bare file list: any other shape flag (`-c`, `-Z`, `--null`, `--json`,
`-q`) changes the line and leaves the list verbatim. Streaming stdin
passthrough is untouched.

Track the fold with real sizes so it shows in `rtk gain`; unchanged
passthrough keeps recording 0/0.

Follow-up to #2630, which targeted the pre-#2641 grep_cmd.rs.
`Ok(2)` at the read-failure site does not say which of diff's three exit
codes it is. The constant states the contract the tests and the docs now
depend on: 2 means an operand could not be read, not that the files differ.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
A folded file list carries its shared prefix in the header line, so
`grep -rl foo src | tail -3` rewritten to `rtk grep` returned tails with no
prefix. List forms (-l/-L/--files, per `is_bare_file_list`) are now left raw
in producer position; other grep/rg forms rewrite as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… test

A hook group whose `matcher` is JSON `null` was counted as not covering the
tool, so `rtk init` appended a second RTK group next to an existing one.
Treat `null` like an omitted matcher, as develop did.

The Copilot integration test ran `rtk hook copilot` from a temp dir whose
project-root walk could reach a `.claude/settings.json` on the test machine,
letting its permission rules change the response. Give the temp dir its own
`.claude` so the walk stops there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The module map in the search-strategy rule still counted 63 TOML filter
configs; src/filters/ now holds 62.
KuSh and others added 21 commits September 28, 2026 01:07
Brings the branch onto the current develop so CI's semgrep baseline is the
develop tip rather than the July merge-base; no source changes beyond the
merge itself.
…ions

fix(discover): distinguish zero-session scans
fix(discover): match Claude project dirs case-insensitively on Windows
…4050

fix(pip): stop doubling the tool name in pip messages
…t like the sibling agents

On develop the rules file handed the agent the full awareness text; the
plugin bundle carried none, so a session got the rewrite and nothing that
says output is condensed or how to recover with rtk proxy. The plugin now
holds rules/AGENTS.md with the file awareness.level selects, plain markdown
with no frontmatter, which Antigravity applies whenever the plugin is
enabled. A re-run rewrites it, so changing the level takes effect.

The plugin files go through the shared atomic_write, and plugin.json, the
file that makes Antigravity discover the directory, is written last: a
write that fails halfway leaves no plugin rather than one missing its
rules. The install output tells the user to restart Antigravity, which only
discovers a plugin directory on startup.

Uninstall reports like Factory Droid: a "not installed (nothing to remove)"
line when there is no plugin, otherwise a header and the removed items, and
the dry-run footer; the helper that removes the directory prints nothing.

README's agent table and the configuration guide no longer describe the
rules file, and the "<1.5ms" figure goes: the hook measures about 7 ms end
to end. Process-level tests pin the printed lines and that both dry runs
change nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(hook): add native Google Antigravity plugin lifecycle and hook support
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…p/actions/upload-artifact-7

chore(deps): bump actions/upload-artifact from 4 to 7
…p/softprops/action-gh-release-3

build(deps): bump softprops/action-gh-release from 2 to 3
…p/actions/github-script-9.0.0

build(deps): bump actions/github-script from 7.1.0 to 9.0.0
…p/actions/stale-11

build(deps): bump actions/stale from 9 to 11
…p/actions/setup-go-7

build(deps): bump actions/setup-go from 5 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
The raw side was measured with 2>&1 and the rtk side with 2>/dev/null, so the two
columns were not the same measurement. rtk forwards the stderr it does not
filter, and dropping that stream books it as if it had been removed.

It also hid the failure mode this fixture is meant to catch. A filter that goes
silent while the tool reports on stderr scored as a saving; the golangci-lint row
read 157 -> - and failed the run for producing nothing, with no way to see that
the diagnostic had gone to the other stream all along.

For an identical measurement rtk is better or equal by construction: stderr is
common to both sides, so never-worse reduces to `filtered <= stdout`.

Measuring stderr also shows when the tool under test is missing. The CI runner
has no rg, so the rg rows compared the shell's "command not found" with rtk's
own error and went negative. The benchmark job now installs ripgrep, and the rg
section is skipped when rg is absent, as the tree section already is.

The run reports 0 negative and 0 fail both with rg installed (rg fn 125360 ->
4083) and without it (section skipped). The good/warn split moves by a row or
two between machines and with what is installed, so it is not a property of this
change.

Verified in golang:1.27 that the go rows read 220 -> 35 GOOD on v2.13.2, and
157 -> 157 -- a tie, not a red run -- if the v2 install were ever reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: stop tests depending on the contributor's locale and repo
`cargo test` wrote into the contributor's own files (tracking database,
tee spool, telemetry salt, agent hooks and settings) and took its results
from their machine: agent settings, exported RTK_* and agent variables, git
configuration and repository variables, files above TMPDIR.

A test build now resolves every user location through `core::user_dirs` and
reads the environment only through `core::user_env`, both answering from a
per-process scratch directory and per-thread values a test sets. Spawned rtk
children get the same redirect through the environment, and every git the
suite runs is isolated from the developer's configuration and repositories.
#3826's helpers move into `test_isolation`, so there is one `rtk_command`.

Scans refuse a test that names the rtk binary outside the two constructors,
and a direct `dirs::`, environment or working-directory read outside
`user_dirs`/`user_env`.

Production behaviour is unchanged; every redirect sits behind cfg(test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bench: measure both sides of every row on stdout+stderr
…p/actions/checkout-7

chore(deps): bump actions/checkout from 4 to 7
Notifies an external pipeline when a pull request from develop to master is
opened or updated, so the release candidate is analysed automatically.

The job is a single authenticated request and nothing else. It checks out no
code, declares no permissions, and exits quietly when the repository has no
pipeline configured, so forks are unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A fork branch also named develop satisfied the previous condition. Secrets are
withheld from fork pull requests so the job exited without acting, but it still
started and consumed runner time, and the safeguard was implicit.

Comparing the head repository with the current one makes the intent explicit
and stops the job from starting at all in that case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(tests): keep the test suite and the developer's environment apart

@aeppling aeppling left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — PR #4228 (develop → master, v0.51.0) @ 4c8a255e

Status: BLOCKED — 1 critical regression. All other layers green: 4076 unit tests, 11/11 changelog features verified on the release binary, real install/uninstall with rc.482 artifacts, exit-code propagation, git status/diff/find/read output byte-identical to v0.50.0.

🔴 BLOCKER — regression (#4125): single-string commands broken in err / test / run / summary

Any command passed as one quoted string — the form that works on v0.50.0 and that existing deployed instructions produce — now fails with exit 127 and rtk: <whole string>: command not found. Exit codes are also wrong ('true && false' returns 127 instead of 1). The error gives no indication of the new syntax, so an agent with pre-upgrade instructions dead-ends and retry-loops.

Reproduce (compare v0.50.0 vs a binary built from this PR):

rtk err 'echo hello'            # 0.50.0: exit 0  → PR: exit 127
rtk err 'true && false'         # 0.50.0: exit 1  → PR: exit 127
rtk err 'cargo --version 2>&1'  # 0.50.0: exit 0  → PR: exit 127
rtk test 'ls /'                 # 0.50.0: exit 0  → PR: exit 127
rtk test 'cd /tmp && pwd'       # 0.50.0: exit 0  → PR: exit 127
rtk summary 'echo a && echo b'  # 0.50.0: exit 0  → PR: exit 127
rtk run 'echo one two'          # 0.50.0: exit 0, prints "one two" → PR: exit 127
rtk err 'echo x | grep x'       # 0.50.0: exit 0  → PR: exit 127

Multi-arg forms (rtk err echo hello, rtk run echo one two) work on both.

Adrien EPPLING and others added 3 commits October 1, 2026 15:22
…orm shell

Direct execution (#4125) made every pre-existing single-string invocation
of err/test/run/summary — the shape all deployed awareness files teach —
die with 127 'command not found' and no way for an agent to self-correct.

Restore exactly that set: when no --shell is given, there is exactly one
argument, it resolves to nothing (127, not an existing-but-unexecutable
126 path), and it is empty or contains a shell metacharacter, run it
through the platform shell as the interposed sh -c used to, with a
one-line stderr note teaching the current syntax. Multi-argument argv
fidelity, resolvable programs, bare-word 127s and 126 classifications
are untouched by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Targeting master from develop is a weak signal on its own. The release pull
request carries the next-release label, applied by the release bot, which states
the intent explicitly rather than inferring it from branch names.

Also reacts to the labeled event, so a pull request opened before the label is
applied still starts the analysis once it arrives, and ignores the labelling of
anything else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ci(transparency): trigger transparency analyze
@CLAassistant

CLAassistant commented Oct 1, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

fix(shell): run unresolvable single-string commands through the platform shell
@aeppling
aeppling merged commit c0f7f01 into master Oct 2, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.