Skip to content

S2b: the nudge that arrives at the sweep moment, carrying the command - #32

Merged
joyful-ii-V-I merged 2 commits into
mainfrom
s2b-sweep-nudge
Aug 12, 2026
Merged

joyful-ii-V-I merged 2 commits into
mainfrom
s2b-sweep-nudge

Conversation

@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator

The first meter-gated behavior round, built from 12h of live data + the month-scale history mine.

The measured problem: the shipped nudge fires (455 calls) and converts at zero — and after the classifier correction below, post-nudge substitution (0.79%) is ~8× LOWER than pre-nudge (6.14%). Generic advice at the wrong moment.

The intervention: sweep detection — the 3rd same-class call in a session (grep/read/glob/git-history; Bash-grep and the Grep tool count as ONE class) triggers one escalated nudge per class per session containing the paste-ready one-call substitute built from the observed pattern (--for="<the agent's own search terms>", --pack-task, --situ). Advisory only, never blocks; RIPWIRE_SWEEP=0 kill switch pre-built; ≤+1ms on the hooked path.

The classifier correction: unclassified 924 → 160 (−82.7%) on a frozen replay — root cause was jq @TSV newline-escaping breaking multi-line cd … && cmd reads, plus cd/pushd/env-var/git-option stripping and new build/gate-run/git-remote classes. Ripwire's own calls were undercounted 8× (9 → 73). Corrected substitution baseline: 5.62%.

Pre-registered efficacy readout (EVALS §4): primary = substitution on post_sweep=1 rows vs the corrected 5.62%; KEEP ≥16.9% (≥3×) / REWORD 8.0–16.8% / DISABLE <8.0%; ≥200 eligible rows across ≥10 sessions or underpowered→extend once→disable. A null readout disables the escalation — the switch exists before the measurement. Confounds disclosed (observational, 100%-treatment log, single-operator). Zero-hit-grep trigger deliberately deferred (needs PostToolUse; two triggers under one readout would be unattributable).

Red-first: 20 arms red against the shipped hook; hookcheck ALL PASS both after. Full plain suite ALL PASS; quality-delta gating=0 (verbosity fixed by splitting the classifier, not acked); determinism/xmllint clean.

🤖 Generated with Claude Code

… command

The meter's first 12 hours said two things at once. The one-time nudges FIRE
— 455 of them — and convert at zero: substitution after a nudge was 0.8%,
indistinguishable from before it. And the dominant behaviour is the SAME-CLASS
SWEEP: grep->grep->grep 357 times as a trigram, read x3 187, git-diff x3 119.
Generic advice at the first grep does not change the next choice.

So: at the Nth call of a class in a session (N=3), replace the verb-and-an-
ellipsis tip with the exact one-call substitute, built from what was observed.
A grep sweep gets a runnable --for= carrying the agent's own last patterns; a
read sweep gets --pack-task/--expand scoped to the directory of the file just
read; a git-history sweep gets --situ; a glob sweep gets --for or the flagless
map. Once per class per session, advisory only, never a blocker, recorded on
the row (nudge=sweep3, post_sweep on everything after) because an unmeasurable
nudge is one nobody can ever turn off on evidence.

The verdict is NOT assumed. docs/EVALS.md §4 pre-registers the one-week
readout, its band (KEEP >=16.9%, REWORD 8.0-16.8%, DISABLE <8.0%), its minimum
data, and the rule that a null readout disables the escalation — which is why
RIPWIRE_SWEEP=0 was built before the measurement rather than after it.

Along the way the instrument turned out to be broken in the direction that
flattered the finding. 924 of a frozen 2,155-row log were `unclassified`, and
742 of those were multi-line or `cd <dir> && ...` command lines: jq's @TSV
escapes newlines, so `cd /a/b\ngit diff` arrived as one token whose basename is
`b\ngit`, matching no rule. Un-escaping for the readers, stripping cd/pushd and
their operand like any other transparent prefix, and skipping git's own
pre-subcommand options takes unclassified 924 -> 160 (-82.7%, 42.9% -> 7.4% of
the log) on replay over that same frozen log. ripwire's own calls were
undercounted 8x (9 -> 73) — the corrected substitution baseline is 5.62%, not
0.8%, and the registration states both numbers and sets its band against the
corrected one. The correction sharpens the motivating finding rather than
dissolving it: under v2 the post-nudge rate (0.79%) is ~8x LOWER than the
pre-nudge rate (6.14%).

Schema v2 adds post_sweep and the sweep<N> nudge value, and widens the
classifier with build / gate-run / git-remote / git-misc / shell-misc. Those
never enter the substitution rate — their families are other/meta/git — they
exist because S4 ranks the absorption queue from the command mix an agent
actually runs, and a class that writes no row is one that survey can never see.
Row counts are therefore NOT comparable across the v1/v2 boundary, which is
what the version field is for.

Cost: one byte appended to a per-class counter plus a fork-free $(<file) read,
on the nudgeable path only. +1 ms or under, below the harness's ±2 ms noise
floor. No call that was already logged got slower; more calls are logged.

Gates: test/hookcheck.sh section (12), arms S1-S14 and C1-C3b — 20 of them red
against the pre-change hook. Fires at N and not before; two calls do not
trigger; a fourth is silent; classes dedup independently; the row carries the
marker; RIPWIRE_SWEEP=0 and the control arm leave calls 1-2 BYTE-IDENTICAL to a
run with the feature on; silence off its preconditions and nothing on stderr;
and fixtures for the cd strip (including `cd x && VAR=y grep` and the
multi-line form) and every new class. 395 gates green, quality-delta gating=0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@joyful-ii-V-I, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67b352d2-7695-4ffd-b876-d2339317c0dc

📥 Commits

Reviewing files that changed from the base of the PR and between 6f83c52 and 157261d.

📒 Files selected for processing (1)
  • docs/EVALS.md
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added configurable sweep-escalation nudges after repeated retrieval calls.
    • Nudges provide paste-ready, class-specific ripwire commands while remaining advisory and allowing the original tool call.
    • Expanded command recognition for Git, build, test, shell, and miscellaneous command types.
    • Improved handling of multiline commands, wrappers, directory changes, and Git options.
  • Documentation

    • Updated substitution-meter guidance to schema v2, including sweep behavior, metrics, configuration, and reporting.
    • Added evaluation criteria and decision thresholds for sweep-escalation nudges.

Walkthrough

The substitution meter now uses schema v2, expands command classification, and adds configurable per-class sweep-escalation nudges. Reporting groups sweep exposure and mixed schemas. Documentation defines the S2b evaluation and decision thresholds. Tests cover classification, escalation, gating, and degradation.

Changes

Substitution meter sweep escalation

Layer / File(s) Summary
Meter schema and command classification
.ripwire_quality_acks, hooks/ripwire-nudge.sh, docs/SUBSTITUTION_METER.md, test/deckcheck_allowlist.txt, test/hookcheck.sh
Meter rows now include schema v2 and post_sweep. Bash normalization and classification cover multiline commands, directory changes, Git options, Git commands, build commands, gate-run commands, and shell-misc commands.
Per-class sweep escalation
hooks/ripwire-nudge.sh, docs/SUBSTITUTION_METER.md, test/hookcheck.sh
The hook tracks per-session, per-class retrieval counts and emits one-time advisory commands at configurable thresholds. Tests cover class isolation, controls, deduplication, repository gating, and unavailable prerequisites.
Exposure reporting and S2b evaluation
bench/substitution_report.py, docs/EVALS.md, docs/SUBSTITUTION_METER.md, test/hookcheck.sh
Reports group rows by sweep exposure and identify mixed schema versions. The S2b registration defines corrected baselines, outcome thresholds, sample requirements, confounds, and evaluation scope.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ToolCall
  participant ripwire-nudge.sh
  participant SweepState
  participant MeterLog
  participant RipwireCommand
  ToolCall->>ripwire-nudge.sh: Send retrieval call
  ripwire-nudge.sh->>SweepState: Update session/class count
  SweepState-->>ripwire-nudge.sh: Return threshold state
  ripwire-nudge.sh->>MeterLog: Write sweep metadata
  ripwire-nudge.sh->>RipwireCommand: Emit class-specific advice
  ripwire-nudge.sh-->>ToolCall: Allow original call
```}

</details>

<!-- walkthrough_end -->
<!-- pre_merge_checks_walkthrough_start -->

<details>
<summary>🚥 Pre-merge checks | ✅ 5</summary>

<details>
<summary>✅ Passed checks (5 passed)</summary>

|         Check name         | Status   | Explanation                                                                                                                 |
| :------------------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------- |
|      Description check     | ✅ Passed | The description directly explains sweep detection, classifier changes, evaluation thresholds, testing, and safety controls. |
|         Title check        | ✅ Passed | The title clearly identifies the S2b sweep-moment nudge and its command payload.                                            |
|     Docstring Coverage     | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.                  |
|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                    |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                    |

</details>

</details>

<!-- pre_merge_checks_walkthrough_end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

<details>
<summary>📝 Generate docstrings</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR
- [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch

</details>
<details>
<summary>🧪 Generate unit tests (beta)</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `s2b-sweep-nudge`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=redhat-et/ripwire&utm_content=32)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>


<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>

<!-- tips_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
Loading

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
test/hookcheck.sh (1)

891-899: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The cd needle makes the C3 doc assertion vacuous.

grep -Fq "cd" matches any word that contains those two letters, for example "recorded" or "included". The check passes even if the cd-prefix rule is removed from the document. Use a distinctive string from the rule, such as cd/pushd or cd x && VAR=y grep.

♻️ Proposed change
-    for needle in 'post_sweep' 'RIPWIRE_SWEEP' 'sweep3' 'shell-misc' 'gate-run' 'git-remote' 'cd'; do
+    for needle in 'post_sweep' 'RIPWIRE_SWEEP' 'sweep3' 'shell-misc' 'gate-run' 'git-remote' '`cd`/`pushd`'; do
🤖 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 `@test/hookcheck.sh` around lines 891 - 899, Update the needle list in the C3
documentation check to replace the ambiguous `cd` substring with a distinctive
rule example such as `cd`/`pushd` or `cd x && VAR=y grep`, while preserving the
existing checks and pass/fail behavior.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@docs/EVALS.md`:
- Around line 569-573: Align the underpowered-readout timing in the “Minimum
data, and what happens below it” section and the readout schedule at “One
readout, at one week” so both describe the same maximum window. Either change
the DISABLE justification from three weeks to two weeks or revise the
extension/schedule to permit three weeks; ensure the registration has only one
unambiguous interpretation.

In `@docs/SUBSTITUTION_METER.md`:
- Around line 246-260: Normalize all frozen-log statistics to the authoritative
pair: 924 total unclassified rows and 742 cd-prefixed rows. Update
docs/SUBSTITUTION_METER.md lines 175-192 and 246-260, hooks/ripwire-nudge.sh
lines 254-260 and 588-599, test/hookcheck.sh lines 812-813, and docs/EVALS.md
lines 528-533; reconcile every conflicting 927, 916, 890, 816, or 784 value
while preserving the 160 corrected total and derived percentages.
- Around line 108-111: Update the documentation reference in
hooks/ripwire-nudge.sh associated with the post_sweep description from
docs/EVALS.md §8 to docs/EVALS.md §4, leaving the surrounding explanation
unchanged.

In `@hooks/ripwire-nudge.sh`:
- Around line 786-795: Sanitize or safely quote the `sweep_dir` value before it
is interpolated into the paste-ready command. Apply this consistently to both
command-generation paths around the `sweep_dir` assignment and the later
emission block at lines 815-832, while preserving the repository-root default
and read-sweep directory behavior.
- Around line 160-174: Resolve meter_arm from RIPWIRE_METER_ARM before the
meter_file early return in meter_init, alongside sweep_on and sweep_n. Preserve
the documented treatment default when the setting is absent, and ensure the
control value is available to the sweep gate even when no writable log path
exists.

---

Nitpick comments:
In `@test/hookcheck.sh`:
- Around line 891-899: Update the needle list in the C3 documentation check to
replace the ambiguous `cd` substring with a distinctive rule example such as
`cd`/`pushd` or `cd x && VAR=y grep`, while preserving the existing checks and
pass/fail behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 82e042ec-4220-4b6f-a0bb-15daaf117c3a

📥 Commits

Reviewing files that changed from the base of the PR and between b74783e and 6f83c52.

📒 Files selected for processing (7)
  • .ripwire_quality_acks
  • bench/substitution_report.py
  • docs/EVALS.md
  • docs/SUBSTITUTION_METER.md
  • hooks/ripwire-nudge.sh
  • test/deckcheck_allowlist.txt
  • test/hookcheck.sh

Comment thread docs/EVALS.md
Comment on lines +569 to +573
**Minimum data, and what happens below it.** ≥ 200 rate-eligible `post_sweep=1` calls across ≥ 10
distinct sessions that saw an escalation. Below that the readout is declared **underpowered** — not
null — the escalation stays on, and the clock extends by one week, once. A second underpowered
readout is a DISABLE: a nudge that cannot accumulate 200 observations in three weeks of daily use is
not reaching enough moments to matter.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The extension window contradicts the DISABLE justification.

Line 571 grants one extension of one week. Line 581 confirms the schedule: "One readout, at one week (or two, under the extension)." Line 572 then justifies the second-underpowered DISABLE with "three weeks of daily use". The maximum elapsed time under the stated rule is two weeks. Correct one of the two statements so the registration cannot be read two ways after the readout.

📝 Proposed fix
-readout is a DISABLE: a nudge that cannot accumulate 200 observations in three weeks of daily use is
+readout is a DISABLE: a nudge that cannot accumulate 200 observations in two weeks of daily use is
 not reaching enough moments to matter.

Also applies to: 581-582

🤖 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 `@docs/EVALS.md` around lines 569 - 573, Align the underpowered-readout timing
in the “Minimum data, and what happens below it” section and the readout
schedule at “One readout, at one week” so both describe the same maximum window.
Either change the DISABLE justification from three weeks to two weeks or revise
the extension/schedule to permit three weeks; ensure the registration has only
one unambiguous interpretation.

Comment on lines +108 to +111
- `post_sweep=1` is the same idea one level up, for the sweep escalation only: everything after an
escalation fired in that session. It is the grouping variable of the pre-registered efficacy
readout in `docs/EVALS.md` §4, and it is recorded at observation time rather than reconstructed,
so the analysis reads an assignment instead of inferring one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List the numbered sections of docs/EVALS.md and every cross-reference to them.
rg -n '^#{2,3} ' docs/EVALS.md
echo '--- references ---'
rg -n 'EVALS\.md §[0-9]+' -g '!docs/EVALS.md' .

Repository: redhat-et/ripwire

Length of output: 9397


🏁 Script executed:

#!/bin/bash
set -eu

echo '--- docs/EVALS.md: section 4 and sweep readout ---'
sed -n '210,235p;500,620p' docs/EVALS.md

echo '--- docs/SUBSTITUTION_METER.md: cited ranges ---'
sed -n '100,160p' docs/SUBSTITUTION_METER.md

echo '--- hooks/ripwire-nudge.sh: cited range ---'
sed -n '108,126p' hooks/ripwire-nudge.sh

echo '--- bench/substitution_report.py: cited range ---'
sed -n '128,150p' bench/substitution_report.py

echo '--- test/hookcheck.sh: cited range ---'
sed -n '624,640p' test/hookcheck.sh

echo '--- exact section-8 references near the documented readouts ---'
rg -n -C 2 '§8|section 8|Section 8|task success|success' docs/EVALS.md hooks/ripwire-nudge.sh bench/substitution_report.py test/hookcheck.sh docs/SUBSTITUTION_METER.md

Repository: redhat-et/ripwire

Length of output: 22513


Change docs/EVALS.md §8 to docs/EVALS.md §4 in hooks/ripwire-nudge.sh. Section 4 registers the sweep-escalation readout. Section 8 covers task-success claims that remain unpublished.

🤖 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 `@docs/SUBSTITUTION_METER.md` around lines 108 - 111, Update the documentation
reference in hooks/ripwire-nudge.sh associated with the post_sweep description
from docs/EVALS.md §8 to docs/EVALS.md §4, leaving the surrounding explanation
unchanged.

Comment on lines +246 to +260
### What the correction did to the headline number

Replaying the v2 classifier over a frozen 2,155-row v1 log:

| | v1, as logged | v2, replayed |
| --- | --- | --- |
| `unclassified` | 924 (42.9% of the log) | **160** (7.4%) — **−82.7%** |
| substitution rate, overall | 1.05% (9/859) | **5.62%** (73/1298) |
| pre-nudge (`post_nudge=0`) | 1.09% | **6.14%** |
| post-nudge (`post_nudge=1`) | 0.79% | **0.79%** |

ripwire's own calls were undercounted **8×** (9 → 73), because `cd <worktree> && ./build/ripwire …`
is exactly the form the prefix strip was missing. Any analysis that quotes the 0.8% headline is
quoting a broken instrument; `docs/EVALS.md` §4 states both numbers and registers its band against
the corrected one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The frozen-log statistics are transcribed with different values in four files. One measurement produced the unclassified total and the cd-prefixed subset, but the total appears as 924, 927, 916, and 890, and the subset appears as 742, 816, and 784. Only 924 is consistent with the derived percentages (924/2155 = 42.9%, (924−160)/924 = −82.7%). Fix each site to the single authoritative pair.

  • docs/SUBSTITUTION_METER.md#L246-L260: keep 924 and 160 as the authoritative totals, and make every other site agree with this table.
  • docs/SUBSTITUTION_METER.md#L175-L192: reconcile "924" on Line 179 with "927" on Line 190, and reconcile the 742 multi-line subset with the 816 cd-prefixed subset.
  • hooks/ripwire-nudge.sh#L254-L260: change "784 of 890 unclassified rows" to the authoritative total and cd-prefixed count.
  • hooks/ripwire-nudge.sh#L588-L599: change "742 of the live log's 916 unclassified rows" to the authoritative total.
  • test/hookcheck.sh#L812-L813: change "816 of the live log's 927 unclassified rows" to the authoritative total and subset.
  • docs/EVALS.md#L528-L533: confirm "924 ... 742 of those" matches the authoritative pair, since this registration is the document the readout will be audited against.
🧰 Tools
🪛 LanguageTool

[style] ~258-~258: Consider an alternative for the overused word “exactly”.
Context: ...cd <worktree> && ./build/ripwire … is exactly the form the prefix strip was missing. ...

(EXACTLY_PRECISELY)

📍 Affects 4 files
  • docs/SUBSTITUTION_METER.md#L246-L260 (this comment)
  • docs/SUBSTITUTION_METER.md#L175-L192
  • hooks/ripwire-nudge.sh#L254-L260
  • hooks/ripwire-nudge.sh#L588-L599
  • test/hookcheck.sh#L812-L813
  • docs/EVALS.md#L528-L533
🤖 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 `@docs/SUBSTITUTION_METER.md` around lines 246 - 260, Normalize all frozen-log
statistics to the authoritative pair: 924 total unclassified rows and 742
cd-prefixed rows. Update docs/SUBSTITUTION_METER.md lines 175-192 and 246-260,
hooks/ripwire-nudge.sh lines 254-260 and 588-599, test/hookcheck.sh lines
812-813, and docs/EVALS.md lines 528-533; reconcile every conflicting 927, 916,
890, 816, or 784 value while preserving the 160 corrected total and derived
percentages.

Comment thread hooks/ripwire-nudge.sh
Comment on lines +160 to +174
# The sweep escalation is resolved even when the LOG is unavailable: it is a nudge, not a
# measurement, and it must not silently switch itself off on a machine with no writable HOME.
case "${RIPWIRE_SWEEP:-$_conf_sweep}" in
0|off|no|false) sweep_on=0 ;;
*) sweep_on=1 ;;
esac
# An N that is not a plain positive integer reads as the default rather than disabling the
# feature by accident or escalating on the first call.
case "${RIPWIRE_SWEEP_N:-$_conf_sweepn}" in
''|*[!0-9]*) sweep_n=3 ;;
0) sweep_n=3 ;;
*) sweep_n="${RIPWIRE_SWEEP_N:-$_conf_sweepn}" ;;
esac

[ -n "$meter_file" ] || return 0 # no HOME and no explicit log path — nothing to write to

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The control arm is not resolved before the sweep gate when no log path exists.

meter_init resolves sweep_on and sweep_n above Line 174, then returns early when meter_file is empty. meter_arm is assigned after that return, so it keeps the treatment default. The sweep gate at Line 729 tests meter_arm != "control". A session that sets RIPWIRE_METER_ARM=control but has no writable log path therefore still escalates. The documented contract says the escalation stays silent in the control arm.

Resolve meter_arm before the early return, next to the sweep settings, for the same reason the comment gives for sweep_on.

🛠️ Proposed fix
+    # The arm is resolved with the sweep settings, and for the same reason: it GATES the nudge, so
+    # it must not depend on whether a log is writable.
+    case "${RIPWIRE_METER_ARM:-$_conf_arm}" in
+        control) meter_arm="control" ;;
+        *)       meter_arm="treatment" ;;
+    esac
+
     [ -n "$meter_file" ] || return 0          # no HOME and no explicit log path — nothing to write to
 
     case "${RIPWIRE_METER:-$_conf_enabled}" in
         0|off|no|false) meter_enabled=0 ;;
         *)              meter_enabled=1 ;;
     esac
-    # Only the literal `control` selects the control arm. An unrecognized value must not silently
-    # invent a third arm — it reads as the default, which is what the row then honestly records.
-    case "${RIPWIRE_METER_ARM:-$_conf_arm}" in
-        control) meter_arm="control" ;;
-        *)       meter_arm="treatment" ;;
-    esac
🤖 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 `@hooks/ripwire-nudge.sh` around lines 160 - 174, Resolve meter_arm from
RIPWIRE_METER_ARM before the meter_file early return in meter_init, alongside
sweep_on and sweep_n. Preserve the documented treatment default when the setting
is absent, and ensure the control value is available to the sweep gate even when
no writable log path exists.

Comment thread hooks/ripwire-nudge.sh
Comment on lines +786 to +795
# The directory the pasted command should be run against: the repo root, except for a read sweep,
# where the directory of the file just read is the tighter and more useful scope. Only an absolute
# path is trusted — a relative one would produce a command that is wrong from anywhere else.
sweep_dir="$meter_repo"
if [ "$mclass" = "read" ]
then
case "$mdetail" in
/*/*) sweep_dir="${mdetail%/*}" ;;
esac
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

sweep_dir is interpolated without the sanitization the grep pattern gets.

Lines 749-759 sanitize the grep pattern at capture time. sweep_dir gets no equivalent treatment. For a read sweep it is ${mdetail%/*}, taken straight from the tool payload, so a path with a space, a quote, or a backtick produces a "paste-ready" command that is not pasteable. The JSON stays valid in both the jq and the fallback branch, so this is a message-quality defect, not an escaping defect.

Consider applying the same character filter to sweep_dir, or quoting the path inside the emitted command.

Also applies to: 815-832

🤖 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 `@hooks/ripwire-nudge.sh` around lines 786 - 795, Sanitize or safely quote the
`sweep_dir` value before it is interpolated into the paste-ready command. Apply
this consistently to both command-generation paths around the `sweep_dir`
assignment and the later emission block at lines 815-832, while preserving the
repository-root default and read-sweep directory behavior.

… live in the local ledger, the public band is defined as multiples of B

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@joyful-ii-V-I
joyful-ii-V-I merged commit efca327 into main Aug 12, 2026
12 checks passed
@joyful-ii-V-I
joyful-ii-V-I deleted the s2b-sweep-nudge branch August 12, 2026 05:33
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.

1 participant