feat(rules): the rule arms stop filtering the corpus to one tier (#3702) #142

Merged
bvandeusen merged 1 commits from dev into main 2026-09-08 00:31:12 -04:00
Owner

Both rule arms passed tier="conditional", on the reasoning — written into semantic_search_rules' docstring — that an always-on rule is already in the session, so surfacing it again is pure noise.

That conflates two different things:

PRESENT IN CONTEXT the rule was delivered at session start
SALIENT AT THE MOMENT the rule is in front of the reader when the action it governs is about to be taken

A rule handed over in a list at turn zero is present while a session writes a config value three hundred turns later. It is not surfaced. So the filter did not skip a redundant hint — it made a whole class of rules permanently ineligible for the only mechanism that puts a rule in front of an agent AT the moment, and the more important a rule is, the likelier it sat in that class.

Underneath, the filter was doing the threshold's job. Whether a rule belongs in a hint is a relevance question and a similarity bar is the control for relevance. A categorical exclusion standing in for a relevance judgment cannot be tuned, cannot be measured, and cannot be wrong in a way anybody notices.

Measured, not settled

The old comment's fear is real: a hint that fires on every write and says obvious things teaches the reader to skip the block, and the surface is lost along with its true positives. It had simply never been checked, and retrieval_logs already records the scores to check it with.

  • rules clear often and high → the fear was justified, the filter was a proxy for a bar set too low, and the bar is the work
  • rules clear rarely, in a thin band → relevance was always sufficient and the filter was never the right instrument

Only eligibility moved. The bar stays at 0.72 and k stays at 1, so the resulting distribution has one cause.

Three comment blocks moved with the code

In this codebase the comments are the specification, and each of these asserted the premise being removed.

  • The 0.72 rationale — its first structural argument was "the eligible corpus is TINY … tier="conditional" rules only". Updated against the change: a larger pool makes clearing the bar mean MORE, so that argument weakened. The bar was left alone anyway rather than move two variables at once.
  • The RULEHINT_LIMIT = 1 rationale — same premise, and it surfaced something the plan had missed: k=1 bounds the blast radius. A wider corpus can change which rule surfaces and how often, never how long one hint gets. The "flood" outcome can therefore only appear as clear-rate.
  • A new block stating the present-vs-salient argument and what each outcome obliges next — so a session that later finds rules flooding knows the filter may legitimately return, but only carrying a measured reason.

test_rule_usage_wiring.py asserted kw["tier"] == "conditional"; it now asserts the absence, pointing at that block rather than carrying a bare expectation.

Baseline

Captured before the deploy boundary and logged on #3702pre_tool_rule 6 cleared of 3,451 calls (0.17%), write_path_rule 33 of 433 (7.6%), both inside a score band 0.033 wide. The three note surfaces are recorded alongside as untouched controls, so movement in them afterwards points at workload rather than at this change.

Milestone #382 · task #3702 · CI run 5961 green on all six lanes.

🤖 Generated with Claude Code

https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ

Both rule arms passed `tier="conditional"`, on the reasoning — written into `semantic_search_rules`' docstring — that an always-on rule is already in the session, so surfacing it again is pure noise. That conflates two different things: | | | |---|---| | **PRESENT IN CONTEXT** | the rule was delivered at session start | | **SALIENT AT THE MOMENT** | the rule is in front of the reader when the action it governs is about to be taken | A rule handed over in a list at turn zero is present while a session writes a config value three hundred turns later. It is not surfaced. So the filter did not skip a redundant hint — it made a whole class of rules **permanently ineligible for the only mechanism that puts a rule in front of an agent AT the moment**, and the more important a rule is, the likelier it sat in that class. Underneath, the filter was doing the **threshold's** job. Whether a rule belongs in a hint is a relevance question and a similarity bar is the control for relevance. A categorical exclusion standing in for a relevance judgment cannot be tuned, cannot be measured, and cannot be wrong in a way anybody notices. ### Measured, not settled The old comment's fear is real: a hint that fires on every write and says obvious things teaches the reader to skip the block, and the surface is lost along with its true positives. It had simply never been checked, and `retrieval_logs` already records the scores to check it with. - rules clear **often and high** → the fear was justified, the filter was a proxy for a bar set too low, and the **bar** is the work - rules clear **rarely, in a thin band** → relevance was always sufficient and the filter was never the right instrument Only eligibility moved. The bar stays at 0.72 and k stays at 1, so the resulting distribution has one cause. ### Three comment blocks moved with the code In this codebase the comments are the specification, and each of these asserted the premise being removed. - **The 0.72 rationale** — its first structural argument was *"the eligible corpus is TINY … `tier="conditional"` rules only"*. Updated *against* the change: a larger pool makes clearing the bar mean MORE, so that argument weakened. The bar was left alone anyway rather than move two variables at once. - **The `RULEHINT_LIMIT = 1` rationale** — same premise, and it surfaced something the plan had missed: **k=1 bounds the blast radius.** A wider corpus can change which rule surfaces and how often, never how long one hint gets. The "flood" outcome can therefore only appear as clear-rate. - **A new block** stating the present-vs-salient argument and what each outcome obliges next — so a session that later finds rules flooding knows the filter may legitimately return, but only carrying a measured reason. `test_rule_usage_wiring.py` asserted `kw["tier"] == "conditional"`; it now asserts the absence, pointing at that block rather than carrying a bare expectation. ### Baseline Captured before the deploy boundary and logged on #3702 — `pre_tool_rule` 6 cleared of 3,451 calls (0.17%), `write_path_rule` 33 of 433 (7.6%), both inside a score band 0.033 wide. The three note surfaces are recorded alongside as untouched controls, so movement in them afterwards points at workload rather than at this change. Milestone #382 · task #3702 · CI run 5961 green on all six lanes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
bvandeusen added 1 commit 2026-09-08 00:31:07 -04:00
feat(rules): the rule arms stop filtering the corpus to one tier (#3702)
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 1m3s
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m7s
CI & Build / Build & push image (push) Successful in 1m29s
dd1e6e2645
Both arms passed tier="conditional", on the reasoning that an always-on
rule is already in the session so re-surfacing it is pure noise. That
conflates two different things:

  PRESENT IN CONTEXT    the rule was delivered at session start
  SALIENT AT THE MOMENT the rule is in front of the reader when the action
                        it governs is about to be taken

A rule handed over in a list at turn zero is present while a session writes
a config value three hundred turns later. It is not surfaced. So the filter
did not skip a redundant hint — it made a whole class of rules permanently
ineligible for the only mechanism that puts a rule in front of an agent AT
the moment, and the more important a rule is, the likelier it sat in that
class.

Underneath, the filter was doing the THRESHOLD's job. Whether a rule belongs
in a hint is a relevance question and a similarity bar is the control for
relevance. A categorical exclusion standing in for a relevance judgment
cannot be tuned, cannot be measured, and cannot be wrong in a way anybody
notices.

MEASURED, NOT SETTLED. The old comment's fear is real: a hint that fires on
every write and says obvious things teaches the reader to skip the block. It
had simply never been checked, and retrieval_logs already records the scores
to check it with. Rules clearing often and high means the fear was justified
and the BAR is the work; rules clearing rarely in a thin band means relevance
was always sufficient.

Only eligibility moved. The bar stays at 0.72 and k stays at 1, so the
resulting distribution has one cause — and k=1 bounds the blast radius: a
wider pool can change which rule surfaces and how often, never how long a
single hint gets.

The threshold rationale's first premise ("the eligible corpus is TINY —
tier=conditional only") is updated rather than deleted: a larger pool makes
clearing the bar mean MORE, so that argument weakened, and the bar was left
alone anyway rather than move two variables at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
bvandeusen merged commit 4a85220aea into main 2026-09-08 00:31:12 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledScribe#142