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
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
name: ci

on:
# The only way to run the rate-limit-guard bench lanes in CI. They are gated
# on BENCH_LANES inside their suite because spawning a lane is running a
# benchmark, and a scheduled lane of their own would be unread advisory
# automation outside `ci-status`. A dispatch runs the full contract corpus,
# so this input only decides whether the lanes run inside it.
workflow_dispatch:
inputs:
bench_lanes:
description: "Run the rate-limit-guard statusline bench lanes"
type: boolean
default: false
push:
branches: [main]
pull_request:
Expand Down Expand Up @@ -1492,6 +1503,9 @@ jobs:
if: needs.changes.outputs.run_tests == 'true'
env:
BASE_REF: ${{ github.base_ref }}
# Empty on every event but a dispatch that asked for the lanes, which
# is what the gate in the bench suite reads as "stay deferred".
BENCH_LANES: ${{ inputs.bench_lanes && '1' || '' }}
run: |
if [ "$GITHUB_EVENT_NAME" != pull_request ]; then
scripts/run-plugin-tests.sh --jobs 3 --shard "$LEG/$LEGS"
Expand Down
2 changes: 1 addition & 1 deletion plugins/rate-limit-guard/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "rate-limit-guard",
"version": "0.8.18",
"version": "0.8.19",
"description": "Shared rate-limit guard for loop lanes: a statusline wrapper tees the subscription rate-limit windows to a fixed machine-scope file, a StopFailure hook records rate-limit stops reactively, and a reader contract fixes how consuming sessions pause and resume.",
"author": {
"name": "Melodic Software",
Expand Down
6 changes: 6 additions & 0 deletions plugins/rate-limit-guard/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
All notable changes to the `rate-limit-guard` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.8.19]

### Changed

- `bench.test.sh` gates its five lane cases on `BENCH_LANES`. Without it the suite runs only the lib-helper assertions and prints a `SKIP:` line naming the coverage that did not run; `BENCH_LANES=1` runs every case, locally or through `ci.yml`'s `bench_lanes` dispatch input.

## [0.8.18]

### Changed
Expand Down
22 changes: 16 additions & 6 deletions plugins/rate-limit-guard/bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,19 @@ HOME="$(mktemp -d)" bash plugins/rate-limit-guard/bench/bench-idle.sh

The **benchmarks gate nothing**. Wall-clock numbers on shared CI runners are noise, so no lane's
timing ever runs in CI. What does run is `bench.test.sh`, a contract smoke suite discovered by
`scripts/run-plugin-tests.sh` like every other `*.test.sh`: it unit-tests the lib helpers and
runs each lane once with tiny parameters against the repo tee under an isolated `HOME`, asserting
behaviour and output shape, never timing. That keeps the harness runnable from a clean checkout
(an unrunnable harness is exactly the defect that made #2521's measurements unreproducible) and
maps these files into `scripts/affected-tests.sh` coverage. The tee's behavioural coverage lives
in `../scripts/statusline-tee.test.sh`.
`scripts/run-plugin-tests.sh` like every other `*.test.sh`, which maps these files into
`scripts/affected-tests.sh` coverage. It splits in two:

- **Every run** unit-tests the lib helpers: `median`, `pace_sleep_arg`, `now_ms` and the refusal
on a bash without `EPOCHREALTIME`. No lane is spawned, so it costs milliseconds.
- **`BENCH_LANES=1`** adds the lane cases: one tiny-parameter run of each lane against the repo
tee under an isolated `HOME`, plus the two failing-render aborts, asserting behaviour and
output shape, never timing. Spawning a lane is running a benchmark whatever the parameters, so
those five cases are gated; without the variable the suite prints a `SKIP:` line and the
runner's summary names the coverage that did not run.

Run them locally with `BENCH_LANES=1 bash plugins/rate-limit-guard/bench/bench.test.sh`, or in CI
by dispatching `ci.yml` with its `bench_lanes` input set. That is the deliberate run that keeps
the harness runnable from a clean checkout; an unrunnable harness is exactly the defect behind the
unreproducible measurements in #2521. The tee's behavioural coverage lives in
`../scripts/statusline-tee.test.sh`.
36 changes: 33 additions & 3 deletions plugins/rate-limit-guard/bench/bench.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
# unrunnable harness is exactly the defect that let #2521's measurements go
# unreproducible (#2582).
#
# The lane runs are GATED on BENCH_LANES: running a lane is running a benchmark
# however small its parameters, so an ordinary CI run stops after the lib
# assertions and reports the lanes as skipped coverage. BENCH_LANES=1 runs them
# — locally, or in CI through ci.yml's `bench_lanes` dispatch input, which is
# the deliberate run the #2582 guard rests on.
#
# Self-contained: defines its own assertion helpers — installed plugins are
# cache-isolated with no shared test lib.

Expand All @@ -26,6 +32,13 @@ ok() {
echo "ok: $*"
PASS=$((PASS + 1))
}
# One summary shape for both exits: the gated stop after the lib assertions and
# the full run.
summary() {
echo
echo "PASS=$PASS FAIL=$FAIL"
[[ $FAIL -eq 0 ]]
}

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
Expand Down Expand Up @@ -77,6 +90,25 @@ else
fail "lib: EPOCHREALTIME guard rc=$RC out=$OUT"
fi

# --- the gate ----------------------------------------------------------------
# Everything above asserts pure functions and costs milliseconds. Everything
# below SPAWNS the lanes, which is a benchmark run whatever the parameters, and
# is the only part of this suite that spends real wall-clock seconds on a
# shared runner.
#
# The deferral prints a SKIP line rather than counting an ok. scripts/
# run-plugin-tests.sh reads `^SKIP:` and names the suite under "Suites with
# skipped coverage (exit 0 here is NOT evidence those cases ran)", which is
# what this is: five cases that did not run. An ok would make the aggregate
# read as full coverage. --strict-skips therefore fails here, correctly — a
# caller declaring a fully provisioned environment is asking for every case to
# run, and BENCH_LANES=1 is how it gets them.
if [[ -z "${BENCH_LANES:-}" ]]; then
echo "SKIP: bench lanes deferred; set BENCH_LANES=1 to run them"
summary
exit
fi

# --- bench-idle: smoke run against the repo tee, isolated HOME ---------------
HOME1="$WORK/home1"
mkdir -p "$HOME1"
Expand Down Expand Up @@ -132,6 +164,4 @@ else
fail "trace-probe: rc=$RC out=${OUT:0:400}"
fi

echo
echo "PASS=$PASS FAIL=$FAIL"
[[ $FAIL -eq 0 ]]
summary
5 changes: 4 additions & 1 deletion scripts/run-plugin-tests-serial.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ plugins/context-guard/hooks/zone-gate.test.sh
# A/B timing harness; its verdicts are elapsed-time comparisons.
plugins/performance/scripts/ab.test.sh

# The statusline bench measures render latency.
# The statusline bench measures render latency. Its lane runs are gated on
# BENCH_LANES, so an ordinary run stops before them and needs no seat here; the
# entry is for the runs that DO set it (a local full corpus with the lanes
# enabled), where the lanes measure exactly what contention distorts.
plugins/rate-limit-guard/bench/bench.test.sh

# Cancellation-window cases kill a parked rename shim on a short fuse.
Expand Down
Loading