M333 made the ranked rule arm measurable. The preload was not, and that is the surface whose value is actually in question. list_always_on_rules, the SessionStart block and every rules_payload caller handed rules over wholesale and emitted nothing — so the resident set's token cost was certain and its usefulness could not be tested even in principle.
Bulk deliveries now record as ambient, reported beside the ranked count and never inside pull_through. Folding them in would mean growing the always-on set depressed the arm's measured precision and trimming it flattered the arm, neither for any reason to do with the arm.
RANKED_SOURCES inverts the note twin's AMBIENT_SOURCES deliberately — naming the rare, slow-moving half means a bulk surface added later and forgotten defaults to ambient (under-counts it) rather than padding the denominator with surfacings nobody chose.
Two lookalike call sites stay silent, with a test to keep them that way: the write-path etag arm and rules_etag_for read the rules to build or compare a marker and show nobody anything.
No migration — event and source are plain Text with no CHECK.
The only just-in-time rule surface was registered on Write|Edit and queried with code or path, so a rule could be retrieved at the moment of a code write and nowhere else. Every rule about which tool to reach for — don't curl the forge, don't stand up a stack, don't run the suite locally — was unreachable exactly when it mattered, and residency in the always-on preload was the only surface it had. That is the pressure that grew the resident set to 31 against note #3089's ceiling of ~23; it was never a judgment anybody made.
A reflex generates no query, so an instruction to check the rules cannot catch one. A mechanical trigger can: the tool call is the query, and a reflex has to become a tool call before it can do anything.
build_tool_rule_hint is tool-agnostic — a name and a string — so widening the matcher later is a hooks.json edit with no server change.
Both pre-tool arms share one session ledger of already-named rules; two would mean a rule named by one arm gets re-offered by the other.
Deliberately silent on outage: a write is occasional, a Bash call is not.
After this merge
Neither piece reaches anywhere on its own:
ships via
needs
2ee24b9 hook
the plugin (marketplace checkout tracks main)
/plugin update → 2026.09.03.0329
8b9b3a1 server
fabledscribe:latest
docker service update --force scribe_app
The pre-tool arm filters to conditional, so rule 161 — the rule it was built for — will not surface until it is re-tiered. That is deliberate and is the next step's work.
Two commits, both halves of the same problem: the rules system could not be judged, and could not be reached at the moment that mattered.
## `8b9b3a1` — the preload emits (#3473)
M333 made the *ranked* rule arm measurable. The preload was not, and that is the surface whose value is actually in question. `list_always_on_rules`, the SessionStart block and every `rules_payload` caller handed rules over wholesale and emitted nothing — so the resident set's token cost was certain and its usefulness could not be tested even in principle.
Bulk deliveries now record as **ambient**, reported beside the ranked count and never inside `pull_through`. Folding them in would mean growing the always-on set depressed the arm's measured precision and trimming it flattered the arm, neither for any reason to do with the arm.
`RANKED_SOURCES` inverts the note twin's `AMBIENT_SOURCES` deliberately — naming the rare, slow-moving half means a bulk surface added later and forgotten defaults to ambient (under-counts it) rather than padding the denominator with surfacings nobody chose.
Two lookalike call sites stay silent, with a test to keep them that way: the write-path etag arm and `rules_etag_for` read the rules to build or compare a **marker** and show nobody anything.
No migration — `event` and `source` are plain Text with no CHECK.
## `2ee24b9` — rules before tools (#3476)
The only just-in-time rule surface was registered on `Write|Edit` and queried with `code or path`, so a rule could be retrieved at the moment of a code write and nowhere else. Every rule about which tool to reach for — don't curl the forge, don't stand up a stack, don't run the suite locally — was unreachable exactly when it mattered, and residency in the always-on preload was the only surface it had. That is the pressure that grew the resident set to 31 against note #3089's ceiling of ~23; it was never a judgment anybody made.
A reflex generates no query, so an instruction to check the rules cannot catch one. A mechanical trigger can: **the tool call is the query**, and a reflex has to become a tool call before it can do anything.
- `build_tool_rule_hint` is tool-agnostic — a name and a string — so widening the matcher later is a `hooks.json` edit with no server change.
- Both pre-tool arms share **one** session ledger of already-named rules; two would mean a rule named by one arm gets re-offered by the other.
- Deliberately silent on outage: a write is occasional, a Bash call is not.
## After this merge
Neither piece reaches anywhere on its own:
| | ships via | needs |
|---|---|---|
| `2ee24b9` hook | the plugin (marketplace checkout tracks `main`) | `/plugin` update → `2026.09.03.0329` |
| `8b9b3a1` server | `fabledscribe:latest` | `docker service update --force scribe_app` |
The pre-tool arm filters to `conditional`, so rule 161 — the rule it was built for — will not surface until it is re-tiered. That is deliberate and is the next step's work.
CI green on `2ee24b9` (run 5433).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
The ranked rule arm became measurable in M333. The preload did not — and
that is the surface whose value is actually in question. `list_always_on_rules`,
the SessionStart block and every `rules_payload` caller handed rules over
wholesale and emitted nothing, so the resident set's token cost was certain
and its usefulness could not be tested even in principle.
Bulk deliveries now record as AMBIENT, beside the ranked count and never
inside pull-through. Folding them in would mean growing the always-on set
depressed the arm's measured precision and trimming it flattered the arm,
neither for any reason to do with the arm.
`RANKED_SOURCES` inverts the note twin's `AMBIENT_SOURCES` deliberately: there
is one ranked rule source and this change adds seven bulk ones, so naming the
rare half makes a forgotten surface default to ambient — under-counting it —
rather than padding the denominator with surfacings nobody chose.
Two lookalike call sites are deliberately left silent, with a test to keep
them that way: the write-path etag arm and `rules_etag_for` read the rules to
build or compare a MARKER and show nobody anything.
No migration — `event` and `source` are plain Text with no CHECK (rule 36
does not apply). Snippet #2858 updated to the new `rules_payload` contract.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
The only just-in-time rule surface was registered on `Write|Edit` and queried
with `code or path`, so a rule could be retrieved at the moment of a code
write and nowhere else. Every rule about which tool to reach for — don't curl
the forge, don't stand up a stack, don't run the suite locally, don't branch —
was unreachable exactly when it mattered, and residency in the always-on
preload was the only surface it had. That is the pressure that grew the
resident set to 31 against #3089's ceiling of ~23; it was never a judgment
anybody made.
A reflex generates no query, so an instruction to check the rules cannot catch
one. A mechanical trigger can: the tool call IS the query, and a reflex has to
become a tool call before it can do anything.
`build_tool_rule_hint` is deliberately tool-agnostic — a name and a string —
so widening the matcher later is a hooks.json edit with no server change. The
hook starts on Bash, which is where the action reflexes live.
The two pre-tool arms share ONE session ledger of already-named rules
(`<state>/<sid>.rules.ids`). Two ledgers would mean a rule named by one arm
gets re-offered by the other, and the hint that fires most often is exactly
the one that must not repeat itself. A test asserts both scripts build the
same path, and another checks the shell hook and the Python route agree on
every query-arg name (rule 33) — a rename there fails silently, looking like
a surface that never finds anything rather than a broken one.
Deliberately silent on outage, unlike the prior-art hook: a write is
occasional, a Bash call is not, and an outage line before every command is
what gets a channel muted.
`tier="conditional"` matches the write arm and is the transition point — an
always-on rule is already resident, so re-tier one and it starts arriving here
instead of in every session's preamble. `pre_tool_rule` joins RANKED_SOURCES:
this arm chose what it showed, so a pull can settle whether the choice landed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two commits, both halves of the same problem: the rules system could not be judged, and could not be reached at the moment that mattered.
8b9b3a1— the preload emits (#3473)M333 made the ranked rule arm measurable. The preload was not, and that is the surface whose value is actually in question.
list_always_on_rules, the SessionStart block and everyrules_payloadcaller handed rules over wholesale and emitted nothing — so the resident set's token cost was certain and its usefulness could not be tested even in principle.Bulk deliveries now record as ambient, reported beside the ranked count and never inside
pull_through. Folding them in would mean growing the always-on set depressed the arm's measured precision and trimming it flattered the arm, neither for any reason to do with the arm.RANKED_SOURCESinverts the note twin'sAMBIENT_SOURCESdeliberately — naming the rare, slow-moving half means a bulk surface added later and forgotten defaults to ambient (under-counts it) rather than padding the denominator with surfacings nobody chose.Two lookalike call sites stay silent, with a test to keep them that way: the write-path etag arm and
rules_etag_forread the rules to build or compare a marker and show nobody anything.No migration —
eventandsourceare plain Text with no CHECK.2ee24b9— rules before tools (#3476)The only just-in-time rule surface was registered on
Write|Editand queried withcode or path, so a rule could be retrieved at the moment of a code write and nowhere else. Every rule about which tool to reach for — don't curl the forge, don't stand up a stack, don't run the suite locally — was unreachable exactly when it mattered, and residency in the always-on preload was the only surface it had. That is the pressure that grew the resident set to 31 against note #3089's ceiling of ~23; it was never a judgment anybody made.A reflex generates no query, so an instruction to check the rules cannot catch one. A mechanical trigger can: the tool call is the query, and a reflex has to become a tool call before it can do anything.
build_tool_rule_hintis tool-agnostic — a name and a string — so widening the matcher later is ahooks.jsonedit with no server change.After this merge
Neither piece reaches anywhere on its own:
2ee24b9hookmain)/pluginupdate →2026.09.03.03298b9b3a1serverfabledscribe:latestdocker service update --force scribe_appThe pre-tool arm filters to
conditional, so rule 161 — the rule it was built for — will not surface until it is re-tiered. That is deliberate and is the next step's work.CI green on
2ee24b9(run 5433).🤖 Generated with Claude Code
https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ