Skip to content

feat(hook): wire TOML filters + better tee tail hint#2748

Merged
aeppling merged 11 commits into
developfrom
feat/toml-filters-in-hook
Jul 3, 2026
Merged

feat(hook): wire TOML filters + better tee tail hint#2748
aeppling merged 11 commits into
developfrom
feat/toml-filters-in-hook

Conversation

@aeppling

@aeppling aeppling commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Wire TOML-covered commands into the transparent hook rewrite path, and make TOML truncation reversible via the shared tee hints.

Related issues & pr

ISSUES

#2179
#820
#2721

PRs

#2226
#894
#2722
#1068

Part 1: Hook wiring (#2179)

The hook only consulted the static RULES table, so commands covered solely by a TOML filter (jj, jq, ssh, …) weren't rewritten. rewrite_segment_inner now falls back to the TOML registry and rewrites them to rtk <cmd> (guarded against denylisted/reserved names). Builds on #2226.

image

Part 2: Reversibility

Truncated TOML output now carries a recovery hint: the line-offset tail hint ([see remaining: tail -n +N …]) for contiguous drops, the full-output hint otherwise, and never an unrecoverable marker (falls back to full raw when tee is off).

image

Then with the command hint
image

Part 3: Trust guards

Both the project .rtk/filters.toml and the user-global ~/.config/rtk/filters.toml are now trust-gated (SHA-256). rtk init prompts to enable detected filters, or --trust-filters / --no-trust-filters for scripts.

Note: the registry load now hits the hook's Unsupported path (~3–8 ms, one-time per process; the Supported path is unaffected) , a match-only RegexSet is a documented follow-up.

"rtk init -g" (--trust-filters or --no-trust-filters available for non interactive)
image

"rtk trust" (--yes available for non interactive)
image

"rtk trust" on global + scoped config
image

Test plan

  • cargo fmt --all && cargo clippy --all-targets && cargo test — fmt clean, zero clippy warnings, 2310 unit + 8 integration tests pass (21 new tests added)
  • Manual testing: rtk <command> output inspected — bare jj log / stat /usr/bin/* / gcc --version transparently rewritten to rtk … by the hook; stat/du emit [see remaining: tail -n +N …] and following it recovers the omitted lines byte-exact; RTK_TEE=0 shows full raw with no truncation marker; git status unchanged and cd / reserved names / RTK_NO_TOML=1 correctly pass through
  • Human testing : Ran rtk commands and created new toml filter to test security gates, all rewrite verified in "rtk gain --history" and tested the recall with the tee_tail hint command
  • Human testing : Tested all UX with interactive or non interactive guards (rtk init -g, rtk trust/untrust)

…cation

Wire TOML-covered commands into the transparent hook rewrite path, and make
TOML truncation reversible via the shared tee hints.

Part 1 — hook wiring (#2179):
The hook's rewrite decision only consulted the static RULES table, so a command
covered solely by a TOML filter (jj, jq, just, ssh, ty, nx, turbo, …) was passed
through unfiltered by the hook even though `rtk <cmd>` filtered it directly.
`rewrite_segment_inner` now consults the TOML registry on a RULES miss
(Unsupported) and rewrites to `rtk <cmd>`, which re-enters via run_fallback →
the TOML tier. Guards: honors RTK_NO_TOML, respects the exclude list, never
rewrites Ignored (denylisted) commands, and a collision guard
(is_rtk_reserved_command) prevents rewriting to a name Clap would route to its
own subcommand. RTK_META_COMMANDS moved to core::constants as the shared source.

Part 2 — reversibility:
TOML truncation on success previously dropped lines with a bare "... omitted"
marker and no recovery pointer. apply_filter_with_info now reports a Lossiness
(Tail / Whole / None); run_fallback emits the line-offset tail hint
(`[see remaining: tail -n +N …]`) for a contiguous tail-drop, the full-output
hint otherwise, and — when tee is unavailable — shows the full raw rather than an
unrecoverable marker. Fixes the symmetric <500B failure-path gap and an
inaccurate MIN_TEE_SIZE comment in curl_cmd.

All hosts (claude/gemini/copilot/cursor) and `rtk rewrite` are covered via the
shared rewrite_command. No hook JSON / integrity-hash changes. The mirrored RULES
rows are kept (they feed discover/session/gain metadata). Builds on the approach
of #2226.

Note: the TOML registry load now lands on the Unsupported hook hot path
(~3-8ms native, one-time per process); the Supported path is unaffected. A
match-only RegexSet to cut that cost is a documented follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aeppling aeppling marked this pull request as draft June 30, 2026 11:47
aeppling added 4 commits July 1, 2026 13:18
…n flags

Trust-gate both filter files; `rtk init` prompts to enable detected filters, or --trust-filters/--no-trust-filters for scripts.
Move the user-facing trust docs into docs/guide (config) and out of CODING_PRACTICES.
The warning printed to stderr on every rewritten command, adding tokens; trust is surfaced only in `rtk init`/`rtk trust`.
Concise filter list + [y/N] (--yes for non-interactive); drops the full-file dump and silent auto-trust. Shared prompt helper with init.
@aeppling aeppling marked this pull request as ready for review July 1, 2026 13:06
@aeppling

aeppling commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Notes

This is a breaking changes for users, custom filters are now gated by rtk trust to avoid security issues.
Need a way to properly inform users as release lands

For now no breaking changes triggered for sem ver but a message in rtk gain

image

aeppling added 3 commits July 1, 2026 15:41
The rewrite path only needs a yes/no match, not the full compiled pipeline find_matching_filter forces on every not-in-RULES command (~10ms/call saved; selection still uses find_matching_filter in run_fallback).
aeppling added 2 commits July 2, 2026 13:35
Recoverable loss for a fired head/max cut (never a bare marker), schema-gate the hook match set, parse-error probe for trust; + tests.

@pszymkowiak pszymkowiak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed and verified locally (built from the branch): cargo fmt clean, cargo clippy --all-targets zero warnings, toml-orphan + trust tests pass.

Part 1 (hook wiring) — functional matrix confirms it: jj / jq / ssh / mise run / just / task rewrite to rtk …; absolute paths, redirects, compound (jj diff && jq .rtk jj diff && rtk jq .), and env-prefix are preserved; cd / reserved / unknown commands are left alone; RTK_NO_TOML=1 disables the TOML fallback while keeping the static RULES.

Part 3 (trust gating) — verified end-to-end: an untrusted .rtk/filters.toml does not activate rewrites until rtk trust, then it does.

Part 2 (tee tail hint) — verified: truncated output carries [see remaining: tail -n +N <teefile>], following it recovers the omitted lines, and RTK_TEE=0 shows full raw with no truncation marker.

Security is sound: collect_match_patterns aggregates only trusted (SHA-256) + built-in filter patterns, so a malicious project filter can't inject a rewrite; reserved / rust-handled commands are excluded from the fallback.

Non-blocking nits: the tee file drops the final trailing newline, so hint recovery is byte-exact except that last \n (looks pre-existing). The ~3–8 ms one-time cost on the Unsupported path justifies the documented match-only RegexSet follow-up.

LGTM 👍

@aeppling aeppling merged commit 31f9d43 into develop Jul 3, 2026
17 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants