Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ result
.direnv/

**/.claude/settings.local.json

AGENTS.override.md

AGENTS.md

.cargo/
.cclsp.json
.mcp.json
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- Generated by scripts/render-agents. Edit /realm/project/intercept-bounce/CLAUDE.md and included files instead. -->
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and display the render-agents script
fd -t f "render-agents" scripts/ --exec cat {}

Repository: Sinity/intercept-bounce

Length of output: 163


🏁 Script executed:

git ls-files | head -20

Repository: Sinity/intercept-bounce

Length of output: 488


🏁 Script executed:

find . -name "render-agents*" -o -name "*render*agents*" 2>/dev/null

Repository: Sinity/intercept-bounce

Length of output: 49


🏁 Script executed:

head -5 AGENTS.md

Repository: Sinity/intercept-bounce

Length of output: 256


🏁 Script executed:

find . -type d -name "scripts" 2>/dev/null

Repository: Sinity/intercept-bounce

Length of output: 49


🏁 Script executed:

rg "render-agents" --type-list

Repository: Sinity/intercept-bounce

Length of output: 457


🏁 Script executed:

rg "render-agents" -t md -t rs -t toml -t sh

Repository: Sinity/intercept-bounce

Length of output: 94


🏁 Script executed:

cat build.rs

Repository: Sinity/intercept-bounce

Length of output: 624


🏁 Script executed:

rg "AGENTS.md" --type-list

Repository: Sinity/intercept-bounce

Length of output: 457


🏁 Script executed:

rg "AGENTS.md" -t md -t rs -t toml -t sh

Repository: Sinity/intercept-bounce

Length of output: 94


🏁 Script executed:

rg "render-agents"

Repository: Sinity/intercept-bounce

Length of output: 49


🏁 Script executed:

rg "AGENTS.md"

Repository: Sinity/intercept-bounce

Length of output: 49


🏁 Script executed:

find . -name "xtask*" -o -name "xtask" 2>/dev/null

Repository: Sinity/intercept-bounce

Length of output: 75


🏁 Script executed:

ls -la | grep -i task

Repository: Sinity/intercept-bounce

Length of output: 124


🏁 Script executed:

find xtask -type f | head -20

Repository: Sinity/intercept-bounce

Length of output: 131


🏁 Script executed:

cat xtask/Cargo.toml

Repository: Sinity/intercept-bounce

Length of output: 488


🏁 Script executed:

ls -la xtask/

Repository: Sinity/intercept-bounce

Length of output: 291


🏁 Script executed:

cat xtask/src/main.rs

Repository: Sinity/intercept-bounce

Length of output: 35133


🏁 Script executed:

git log --oneline AGENTS.md | head -10

Repository: Sinity/intercept-bounce

Length of output: 119


🏁 Script executed:

rg "AGENTS" --type md

Repository: Sinity/intercept-bounce

Length of output: 49


Use a relative path instead of an absolute path in the generated comment.

The comment in AGENTS.md references /realm/project/intercept-bounce/CLAUDE.md, which is a machine-specific absolute path. Other developers won't have this exact path structure, making the comment less helpful and the generated file less portable.

Update the comment to use a relative path:

Suggested fix
<!-- Generated by scripts/render-agents. Edit CLAUDE.md and included files instead. -->
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` at line 1, Replace the machine-specific absolute path in the
generated header comment in AGENTS.md by using a relative filename; specifically
locate the generated comment that currently contains
"/realm/project/intercept-bounce/CLAUDE.md" and change it to reference
"CLAUDE.md" (or "CLAUDE.md and included files" as in the suggested fix) so the
header reads like the output of scripts/render-agents but uses a relative path
instead of the absolute one.


# Repository Guidelines

## Project Structure & Module Organization
Expand Down
26 changes: 26 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Repository Guidelines

## Project Structure & Module Organization
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider adding blank lines after headings to satisfy markdownlint (MD022).

The markdownlint static analyzer reports missing blank lines between headings and content at multiple locations. While your pre-commit hooks passed (suggesting this may be acceptable in your project's style), standard markdown convention and MD022 expect a blank line after each heading.

If you want to satisfy the linter, add a blank line after each ## heading:

📝 Example fix for line 3
 ## Project Structure & Module Organization
+
 The runtime crate lives under `src/`, where `main.rs` wires the CLI in `cli.rs` to the filtering core in `filter/`.

Apply the same pattern to the headings at lines 6, 15, 18, 22, and 25.

Also applies to: 6-6, 15-15, 18-18, 22-22, 25-25

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` at line 3, Add a blank line immediately after each second-level
heading to satisfy markdownlint MD022: for example insert one empty line after
the heading "## Project Structure & Module Organization" and do the same for the
other `##` headings referenced (lines noted in the review). Locate the `##`
headings in CLAUDE.md (e.g., "## Project Structure & Module Organization") and
ensure there is exactly one blank line between the heading and the following
paragraph or content throughout the file.

The runtime crate lives under `src/`, where `main.rs` wires the CLI in `cli.rs` to the filtering core in `filter/`. Shared utilities (`config.rs`, `telemetry.rs`, `logger.rs`) keep side-effects isolated. Integration and property tests sit in `tests/`, with reusable fixtures in `test-helpers/`. Performance work belongs in `benches/`, while `fuzz/` houses `cargo fuzz` targets. Reference material and generated manuals land in `docs/`, and the `xtask/` helper orchestrates doc builds.

## Build, Test, and Development Commands
- `cargo build --release` compiles the filter binary with production optimizations.
- `cargo test --all` runs unit, integration, and property suites; mirror CI before opening a PR.
- `cargo clippy --all-targets -- -D warnings` enforces lint cleanliness; match CI expectations.
- `cargo fmt --all` or `./dev.sh fmt` keeps formatting consistent; run before committing.
- `./dev.sh all` chains fmt, clippy, and tests for a quick gate.
- `cargo run --package xtask --bin xtask -- generate-docs` refreshes the CLI/manpage material in `docs/`.
- `nix develop` drops you into the flake-provisioned dev shell; use `./dev.sh nix` for a full build.

## Coding Style & Naming Conventions
Rust code follows standard 4-space indentation and `rustfmt` defaults. Modules, files, and test helpers use `snake_case`; types remain `PascalCase`. Prefer explicit `use crate::...` paths for clarity, and document non-obvious helpers with `///` comments. CI treats clippy warnings as errors, so resolve lints locally.

## Runtime Configuration Tips
- `--ignore-key <KEY>` lets you exempt specific controls (e.g., `KEY_VOLUMEDOWN` encoder wheels) from debouncing; the flag accepts symbolic names or numeric codes.
- Keep `--log-bounces` on when developing input pipelines so freshly ignored keys can be verified quickly via the systemd journal.

## Testing Guidelines
Keep fast checks in `tests/` and push slower fuzz targets under `fuzz/`. Name new integration files `*_tests.rs`; within modules, group cases under `mod tests { ... }`. Run `cargo test --all` before pushing, and refresh relevant property tests when touching timing logic. For fuzzing, seed with `cargo fuzz run fuzz_core_filter` or `fuzz_target_stats` and commit any minimized corpus updates. Benchmarks live in `benches/filter.rs`; use `cargo bench` to validate performance-sensitive changes.

## Commit & Pull Request Guidelines
History favors concise, imperative subjects (e.g., `Tighten near-miss logging`); include a brief body when context is not obvious. Squash fixups locally so each commit passes `./dev.sh all`. PRs should link issues when applicable, summarize behavioural impact, and cite how you validated the change. Attach log snippets or screenshots for user-visible output shifts, and call out configuration updates in `docs/` or sample pipelines.
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 12 additions & 12 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@
})
// {
nixosModules = {
default = import ./nix/modules/intercept-bounce.nix { inherit self; };
intercept-bounce = import ./nix/modules/intercept-bounce.nix { inherit self; };
default = import ./nix/modules/intercept-bounce.nix {inherit self;};
intercept-bounce = import ./nix/modules/intercept-bounce.nix {inherit self;};
};
};
}
54 changes: 30 additions & 24 deletions nix/modules/intercept-bounce.nix
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{ self }:
{ config, lib, pkgs, ... }:
let
inherit (lib)
{self}: {
config,
lib,
pkgs,
...
}: let
inherit
(lib)
concatMap
escapeShellArgs
mkEnableOption
Expand All @@ -11,7 +15,7 @@ let
types
;

packagesForSystem = lib.attrByPath [ pkgs.system ] (self.packages or { }) { };
packagesForSystem = lib.attrByPath [pkgs.system] (self.packages or {}) {};

defaultPackage =
packagesForSystem.intercept-bounce
Expand All @@ -21,37 +25,39 @@ let
cfg = config.services.interceptBounce;

toStr = value:
if builtins.isString value then value else builtins.toString value;
if builtins.isString value
then value
else builtins.toString value;

baseArgs =
[]
++ optionals (cfg.debounceTime != null) [ "--debounce-time" cfg.debounceTime ]
++ optionals (cfg.debounceTime != null) ["--debounce-time" cfg.debounceTime]
++ optionals (cfg.nearMissThresholdTime != null) [
"--near-miss-threshold-time"
cfg.nearMissThresholdTime
]
++ optionals (cfg.logInterval != null) [ "--log-interval" cfg.logInterval ]
++ optionals cfg.logAllEvents [ "--log-all-events" ]
++ optionals cfg.logBounces [ "--log-bounces" ]
++ optionals cfg.listDevices [ "--list-devices" ]
++ optionals cfg.statsJson [ "--stats-json" ]
++ optionals cfg.verbose [ "--verbose" ]
++ optionals (cfg.logInterval != null) ["--log-interval" cfg.logInterval]
++ optionals cfg.logAllEvents ["--log-all-events"]
++ optionals cfg.logBounces ["--log-bounces"]
++ optionals cfg.listDevices ["--list-devices"]
++ optionals cfg.statsJson ["--stats-json"]
++ optionals cfg.verbose ["--verbose"]
++ optionals (cfg.ringBufferSize != null) [
"--ring-buffer-size"
toStr cfg.ringBufferSize
toStr
cfg.ringBufferSize
]
Comment on lines 45 to 49
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification: inspect the ring-buffer-size argument construction.
set -euo pipefail

rg -n -A6 -B2 'optionals \(cfg\.ringBufferSize != null\)' nix/modules/intercept-bounce.nix
rg -n '^\s*toStr\s*$|^\s*cfg\.ringBufferSize\s*$|toStr\s+cfg\.ringBufferSize' nix/modules/intercept-bounce.nix

Repository: Sinity/intercept-bounce

Length of output: 470


🏁 Script executed:

#!/bin/bash
# Verify toStr definition and baseArgs construction/usage
set -euo pipefail

# Find toStr definition
echo "=== Search for toStr definition ==="
rg -n -B3 -A3 'toStr\s*=' nix/modules/intercept-bounce.nix

# Find where baseArgs is built and used
echo "=== Search for baseArgs construction and usage ==="
rg -n -B2 -A10 'baseArgs\s*=' nix/modules/intercept-bounce.nix

# Find escapeShellArgs calls
echo "=== Search for escapeShellArgs usage ==="
rg -n -B2 -A2 'escapeShellArgs' nix/modules/intercept-bounce.nix

Repository: Sinity/intercept-bounce

Length of output: 1158


toStr is passed as a list element instead of being applied (causes evaluation failure).

At lines 47–48, toStr and cfg.ringBufferSize are separate list items. The function toStr is not applied, so a function value is included in baseArgs. When commandList is passed to escapeShellArgs (line 59), it fails because escapeShellArgs cannot escape a function value. This should be (toStr cfg.ringBufferSize) as a single element.

Proposed fix
     ++ optionals (cfg.ringBufferSize != null) [
       "--ring-buffer-size"
-      toStr
-      cfg.ringBufferSize
+      (toStr cfg.ringBufferSize)
     ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
++ optionals (cfg.ringBufferSize != null) [
"--ring-buffer-size"
toStr cfg.ringBufferSize
toStr
cfg.ringBufferSize
]
+ optionals (cfg.ringBufferSize != null) [
"--ring-buffer-size"
(toStr cfg.ringBufferSize)
]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nix/modules/intercept-bounce.nix` around lines 45 - 49, The list under
optionals incorrectly includes the function toStr as a separate list element
instead of applying it to cfg.ringBufferSize, causing a function value to end up
in baseArgs/commandList and breaking escapeShellArgs; fix by replacing the
separate toStr and cfg.ringBufferSize list entries with a single element that
applies toStr to cfg.ringBufferSize (i.e., use the result of toStr
cfg.ringBufferSize), so baseArgs/commandList contain only strings when passed to
escapeShellArgs.

++ concatMap (key: [ "--debounce-key" key ]) cfg.debounceKeys
++ concatMap (key: [ "--ignore-key" key ]) cfg.ignoreKeys
++ concatMap (key: ["--debounce-key" key]) cfg.debounceKeys
++ concatMap (key: ["--ignore-key" key]) cfg.ignoreKeys
++ optionals (cfg.otelEndpoint != null) [
"--otel-endpoint"
cfg.otelEndpoint
]
++ cfg.extraArgs;

commandList = [ "${cfg.package}/bin/intercept-bounce" ] ++ baseArgs;
commandList = ["${cfg.package}/bin/intercept-bounce"] ++ baseArgs;
commandString = escapeShellArgs commandList;
in
{
in {
options.services.interceptBounce = {
enable = mkEnableOption "intercept-bounce CLI integration";

Expand Down Expand Up @@ -126,13 +132,13 @@ in

debounceKeys = mkOption {
type = types.listOf types.str;
default = [ ];
default = [];
description = "List of keys passed via repeated --debounce-key flags.";
};

ignoreKeys = mkOption {
type = types.listOf types.str;
default = [ ];
default = [];
description = "List of keys passed via repeated --ignore-key flags.";
};

Expand All @@ -144,13 +150,13 @@ in

extraArgs = mkOption {
type = types.listOf types.str;
default = [ ];
default = [];
description = "Arbitrary arguments appended after generated flags.";
};

command = mkOption {
type = types.listOf types.str;
default = [ ];
default = [];
description = "Resolved intercept-bounce invocation expressed as a list.";
};

Expand All @@ -162,7 +168,7 @@ in
};

config = mkIf cfg.enable {
environment.systemPackages = optionals cfg.installSystemPackage [ cfg.package ];
environment.systemPackages = optionals cfg.installSystemPackage [cfg.package];
services.interceptBounce.command = commandList;
services.interceptBounce.commandString = commandString;
};
Expand Down
6 changes: 1 addition & 5 deletions src/filter/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,7 @@ impl TimingHistogram {

/// Calculates the average timing in microseconds. Returns 0 if count is 0.
pub fn average_us(&self) -> u64 {
if self.count > 0 {
self.sum_us / self.count
} else {
0
}
self.sum_us.checked_div(self.count).unwrap_or(0)
}

// Add methods like get_buckets(), get_count() if needed externally.
Expand Down
Loading